diff options
author | Tiger Yang <tiger.yang@oracle.com> | 2008-08-18 05:11:00 -0400 |
---|---|---|
committer | Mark Fasheh <mfasheh@suse.com> | 2008-10-13 19:57:02 -0400 |
commit | cf1d6c763fbcb115263114302485ad17e7933d87 (patch) | |
tree | 85717dc9d20b7ac1e31b683653933f2d30099f44 /fs | |
parent | fdd77704a8b4666a32120fcd1e4a9fedaf3263d8 (diff) |
ocfs2: Add extended attribute support
This patch implements storing extended attributes both in inode or a single
external block. We only store EA's in-inode when blocksize > 512 or that
inode block has free space for it. When an EA's value is larger than 80
bytes, we will store the value via b-tree outside inode or block.
Signed-off-by: Tiger Yang <tiger.yang@oracle.com>
Signed-off-by: Mark Fasheh <mfasheh@suse.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ocfs2/Makefile | 2 | ||||
-rw-r--r-- | fs/ocfs2/file.c | 5 | ||||
-rw-r--r-- | fs/ocfs2/inode.c | 8 | ||||
-rw-r--r-- | fs/ocfs2/inode.h | 3 | ||||
-rw-r--r-- | fs/ocfs2/journal.h | 10 | ||||
-rw-r--r-- | fs/ocfs2/namei.c | 5 | ||||
-rw-r--r-- | fs/ocfs2/ocfs2.h | 2 | ||||
-rw-r--r-- | fs/ocfs2/ocfs2_fs.h | 8 | ||||
-rw-r--r-- | fs/ocfs2/suballoc.c | 17 | ||||
-rw-r--r-- | fs/ocfs2/suballoc.h | 3 | ||||
-rw-r--r-- | fs/ocfs2/super.c | 14 | ||||
-rw-r--r-- | fs/ocfs2/symlink.c | 9 | ||||
-rw-r--r-- | fs/ocfs2/xattr.c | 1620 | ||||
-rw-r--r-- | fs/ocfs2/xattr.h | 51 | ||||
-rw-r--r-- | fs/ocfs2/xattr_trusted.c | 82 | ||||
-rw-r--r-- | fs/ocfs2/xattr_user.c | 94 |
16 files changed, 1927 insertions, 6 deletions
diff --git a/fs/ocfs2/Makefile b/fs/ocfs2/Makefile index af63980319c3..21323da40855 100644 --- a/fs/ocfs2/Makefile +++ b/fs/ocfs2/Makefile | |||
@@ -36,6 +36,8 @@ ocfs2-objs := \ | |||
36 | uptodate.o \ | 36 | uptodate.o \ |
37 | ver.o \ | 37 | ver.o \ |
38 | xattr.o \ | 38 | xattr.o \ |
39 | xattr_user.o \ | ||
40 | xattr_trusted.o | ||
39 | 41 | ||
40 | ocfs2_stackglue-objs := stackglue.o | 42 | ocfs2_stackglue-objs := stackglue.o |
41 | ocfs2_stack_o2cb-objs := stack_o2cb.o | 43 | ocfs2_stack_o2cb-objs := stack_o2cb.o |
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 89d8541f85b5..f4273c2c2095 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c | |||
@@ -55,6 +55,7 @@ | |||
55 | #include "mmap.h" | 55 | #include "mmap.h" |
56 | #include "suballoc.h" | 56 | #include "suballoc.h" |
57 | #include "super.h" | 57 | #include "super.h" |
58 | #include "xattr.h" | ||
58 | 59 | ||
59 | #include "buffer_head_io.h" | 60 | #include "buffer_head_io.h" |
60 | 61 | ||
@@ -2070,6 +2071,10 @@ const struct inode_operations ocfs2_file_iops = { | |||
2070 | .setattr = ocfs2_setattr, | 2071 | .setattr = ocfs2_setattr, |
2071 | .getattr = ocfs2_getattr, | 2072 | .getattr = ocfs2_getattr, |
2072 | .permission = ocfs2_permission, | 2073 | .permission = ocfs2_permission, |
2074 | .setxattr = generic_setxattr, | ||
2075 | .getxattr = generic_getxattr, | ||
2076 | .listxattr = ocfs2_listxattr, | ||
2077 | .removexattr = generic_removexattr, | ||
2073 | .fallocate = ocfs2_fallocate, | 2078 | .fallocate = ocfs2_fallocate, |
2074 | .fiemap = ocfs2_fiemap, | 2079 | .fiemap = ocfs2_fiemap, |
2075 | }; | 2080 | }; |
diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index 99f012a0f207..4738dd25bb94 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c | |||
@@ -49,6 +49,7 @@ | |||
49 | #include "symlink.h" | 49 | #include "symlink.h" |
50 | #include "sysfile.h" | 50 | #include "sysfile.h" |
51 | #include "uptodate.h" | 51 | #include "uptodate.h" |
52 | #include "xattr.h" | ||
52 | 53 | ||
53 | #include "buffer_head_io.h" | 54 | #include "buffer_head_io.h" |
54 | 55 | ||
@@ -741,6 +742,13 @@ static int ocfs2_wipe_inode(struct inode *inode, | |||
741 | goto bail_unlock_dir; | 742 | goto bail_unlock_dir; |
742 | } | 743 | } |
743 | 744 | ||
745 | /*Free extended attribute resources associated with this inode.*/ | ||
746 | status = ocfs2_xattr_remove(inode, di_bh); | ||
747 | if (status < 0) { | ||
748 | mlog_errno(status); | ||
749 | goto bail_unlock_dir; | ||
750 | } | ||
751 | |||
744 | status = ocfs2_remove_inode(inode, di_bh, orphan_dir_inode, | 752 | status = ocfs2_remove_inode(inode, di_bh, orphan_dir_inode, |
745 | orphan_dir_bh); | 753 | orphan_dir_bh); |
746 | if (status < 0) | 754 | if (status < 0) |
diff --git a/fs/ocfs2/inode.h b/fs/ocfs2/inode.h index 390a85596aa0..499bc62e758b 100644 --- a/fs/ocfs2/inode.h +++ b/fs/ocfs2/inode.h | |||
@@ -40,6 +40,9 @@ struct ocfs2_inode_info | |||
40 | /* protects allocation changes on this inode. */ | 40 | /* protects allocation changes on this inode. */ |
41 | struct rw_semaphore ip_alloc_sem; | 41 | struct rw_semaphore ip_alloc_sem; |
42 | 42 | ||
43 | /* protects extended attribute changes on this inode */ | ||
44 | struct rw_semaphore ip_xattr_sem; | ||
45 | |||
43 | /* These fields are protected by ip_lock */ | 46 | /* These fields are protected by ip_lock */ |
44 | spinlock_t ip_lock; | 47 | spinlock_t ip_lock; |
45 | u32 ip_open_count; | 48 | u32 ip_open_count; |
diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h index 9485f8037d9b..08d1add14872 100644 --- a/fs/ocfs2/journal.h +++ b/fs/ocfs2/journal.h | |||
@@ -283,6 +283,9 @@ int ocfs2_journal_dirty_data(handle_t *handle, | |||
283 | /* simple file updates like chmod, etc. */ | 283 | /* simple file updates like chmod, etc. */ |
284 | #define OCFS2_INODE_UPDATE_CREDITS 1 | 284 | #define OCFS2_INODE_UPDATE_CREDITS 1 |
285 | 285 | ||
286 | /* extended attribute block update */ | ||
287 | #define OCFS2_XATTR_BLOCK_UPDATE_CREDITS 1 | ||
288 | |||
286 | /* group extend. inode update and last group update. */ | 289 | /* group extend. inode update and last group update. */ |
287 | #define OCFS2_GROUP_EXTEND_CREDITS (OCFS2_INODE_UPDATE_CREDITS + 1) | 290 | #define OCFS2_GROUP_EXTEND_CREDITS (OCFS2_INODE_UPDATE_CREDITS + 1) |
288 | 291 | ||
@@ -340,6 +343,13 @@ int ocfs2_journal_dirty_data(handle_t *handle, | |||
340 | #define OCFS2_RENAME_CREDITS (3 * OCFS2_INODE_UPDATE_CREDITS + 3 \ | 343 | #define OCFS2_RENAME_CREDITS (3 * OCFS2_INODE_UPDATE_CREDITS + 3 \ |
341 | + OCFS2_UNLINK_CREDITS) | 344 | + OCFS2_UNLINK_CREDITS) |
342 | 345 | ||
346 | /* global bitmap dinode, group desc., relinked group, | ||
347 | * suballocator dinode, group desc., relinked group, | ||
348 | * dinode, xattr block */ | ||
349 | #define OCFS2_XATTR_BLOCK_CREATE_CREDITS (OCFS2_SUBALLOC_ALLOC * 2 + \ | ||
350 | + OCFS2_INODE_UPDATE_CREDITS \ | ||
351 | + OCFS2_XATTR_BLOCK_UPDATE_CREDITS) | ||
352 | |||
343 | /* | 353 | /* |
344 | * Please note that the caller must make sure that root_el is the root | 354 | * Please note that the caller must make sure that root_el is the root |
345 | * of extent tree. So for an inode, it should be &fe->id2.i_list. Otherwise | 355 | * of extent tree. So for an inode, it should be &fe->id2.i_list. Otherwise |
diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index 2cd6f501755e..76d1d1314308 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c | |||
@@ -60,6 +60,7 @@ | |||
60 | #include "symlink.h" | 60 | #include "symlink.h" |
61 | #include "sysfile.h" | 61 | #include "sysfile.h" |
62 | #include "uptodate.h" | 62 | #include "uptodate.h" |
63 | #include "xattr.h" | ||
63 | 64 | ||
64 | #include "buffer_head_io.h" | 65 | #include "buffer_head_io.h" |
65 | 66 | ||
@@ -1918,4 +1919,8 @@ const struct inode_operations ocfs2_dir_iops = { | |||
1918 | .setattr = ocfs2_setattr, | 1919 | .setattr = ocfs2_setattr, |
1919 | .getattr = ocfs2_getattr, | 1920 | .getattr = ocfs2_getattr, |
1920 | .permission = ocfs2_permission, | 1921 | .permission = ocfs2_permission, |
1922 | .setxattr = generic_setxattr, | ||
1923 | .getxattr = generic_getxattr, | ||
1924 | .listxattr = ocfs2_listxattr, | ||
1925 | .removexattr = generic_removexattr, | ||
1921 | }; | 1926 | }; |
diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h index ce75ca312a2d..cae0dd4b7f75 100644 --- a/fs/ocfs2/ocfs2.h +++ b/fs/ocfs2/ocfs2.h | |||
@@ -188,6 +188,7 @@ enum ocfs2_mount_options | |||
188 | OCFS2_MOUNT_ERRORS_PANIC = 1 << 3, /* Panic on errors */ | 188 | OCFS2_MOUNT_ERRORS_PANIC = 1 << 3, /* Panic on errors */ |
189 | OCFS2_MOUNT_DATA_WRITEBACK = 1 << 4, /* No data ordering */ | 189 | OCFS2_MOUNT_DATA_WRITEBACK = 1 << 4, /* No data ordering */ |
190 | OCFS2_MOUNT_LOCALFLOCKS = 1 << 5, /* No cluster aware user file locks */ | 190 | OCFS2_MOUNT_LOCALFLOCKS = 1 << 5, /* No cluster aware user file locks */ |
191 | OCFS2_MOUNT_NOUSERXATTR = 1 << 6, /* No user xattr */ | ||
191 | }; | 192 | }; |
192 | 193 | ||
193 | #define OCFS2_OSB_SOFT_RO 0x0001 | 194 | #define OCFS2_OSB_SOFT_RO 0x0001 |
@@ -218,6 +219,7 @@ struct ocfs2_super | |||
218 | u32 bitmap_cpg; | 219 | u32 bitmap_cpg; |
219 | u8 *uuid; | 220 | u8 *uuid; |
220 | char *uuid_str; | 221 | char *uuid_str; |
222 | u32 uuid_hash; | ||
221 | u8 *vol_label; | 223 | u8 *vol_label; |
222 | u64 first_cluster_group_blkno; | 224 | u64 first_cluster_group_blkno; |
223 | u32 fs_generation; | 225 | u32 fs_generation; |
diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h index 1055ba0af9bb..98e1f8bba0e1 100644 --- a/fs/ocfs2/ocfs2_fs.h +++ b/fs/ocfs2/ocfs2_fs.h | |||
@@ -570,7 +570,7 @@ struct ocfs2_super_block { | |||
570 | /*40*/ __le16 s_max_slots; /* Max number of simultaneous mounts | 570 | /*40*/ __le16 s_max_slots; /* Max number of simultaneous mounts |
571 | before tunefs required */ | 571 | before tunefs required */ |
572 | __le16 s_tunefs_flag; | 572 | __le16 s_tunefs_flag; |
573 | __le32 s_reserved1; | 573 | __le32 s_uuid_hash; /* hash value of uuid */ |
574 | __le64 s_first_cluster_group; /* Block offset of 1st cluster | 574 | __le64 s_first_cluster_group; /* Block offset of 1st cluster |
575 | * group header */ | 575 | * group header */ |
576 | /*50*/ __u8 s_label[OCFS2_MAX_VOL_LABEL_LEN]; /* Label for mounting, etc. */ | 576 | /*50*/ __u8 s_label[OCFS2_MAX_VOL_LABEL_LEN]; /* Label for mounting, etc. */ |
@@ -787,7 +787,11 @@ struct ocfs2_xattr_tree_root { | |||
787 | /*10*/ struct ocfs2_extent_list xt_list; /* Extent record list */ | 787 | /*10*/ struct ocfs2_extent_list xt_list; /* Extent record list */ |
788 | }; | 788 | }; |
789 | 789 | ||
790 | #define OCFS2_XATTR_INDEXED 0x1 | 790 | #define OCFS2_XATTR_INDEXED 0x1 |
791 | #define OCFS2_HASH_SHIFT 5 | ||
792 | #define OCFS2_XATTR_ROUND 3 | ||
793 | #define OCFS2_XATTR_SIZE(size) (((size) + OCFS2_XATTR_ROUND) & \ | ||
794 | ~(OCFS2_XATTR_ROUND)) | ||
791 | 795 | ||
792 | /* | 796 | /* |
793 | * On disk structure for xattr block. | 797 | * On disk structure for xattr block. |
diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c index bb774d70d268..f1871ca83815 100644 --- a/fs/ocfs2/suballoc.c +++ b/fs/ocfs2/suballoc.c | |||
@@ -493,9 +493,9 @@ bail: | |||
493 | return status; | 493 | return status; |
494 | } | 494 | } |
495 | 495 | ||
496 | int ocfs2_reserve_new_metadata(struct ocfs2_super *osb, | 496 | int ocfs2_reserve_new_metadata_blocks(struct ocfs2_super *osb, |
497 | struct ocfs2_extent_list *root_el, | 497 | int blocks, |
498 | struct ocfs2_alloc_context **ac) | 498 | struct ocfs2_alloc_context **ac) |
499 | { | 499 | { |
500 | int status; | 500 | int status; |
501 | u32 slot; | 501 | u32 slot; |
@@ -507,7 +507,7 @@ int ocfs2_reserve_new_metadata(struct ocfs2_super *osb, | |||
507 | goto bail; | 507 | goto bail; |
508 | } | 508 | } |
509 | 509 | ||
510 | (*ac)->ac_bits_wanted = ocfs2_extend_meta_needed(root_el); | 510 | (*ac)->ac_bits_wanted = blocks; |
511 | (*ac)->ac_which = OCFS2_AC_USE_META; | 511 | (*ac)->ac_which = OCFS2_AC_USE_META; |
512 | slot = osb->slot_num; | 512 | slot = osb->slot_num; |
513 | (*ac)->ac_group_search = ocfs2_block_group_search; | 513 | (*ac)->ac_group_search = ocfs2_block_group_search; |
@@ -532,6 +532,15 @@ bail: | |||
532 | return status; | 532 | return status; |
533 | } | 533 | } |
534 | 534 | ||
535 | int ocfs2_reserve_new_metadata(struct ocfs2_super *osb, | ||
536 | struct ocfs2_extent_list *root_el, | ||
537 | struct ocfs2_alloc_context **ac) | ||
538 | { | ||
539 | return ocfs2_reserve_new_metadata_blocks(osb, | ||
540 | ocfs2_extend_meta_needed(root_el), | ||
541 | ac); | ||
542 | } | ||
543 | |||
535 | static int ocfs2_steal_inode_from_other_nodes(struct ocfs2_super *osb, | 544 | static int ocfs2_steal_inode_from_other_nodes(struct ocfs2_super *osb, |
536 | struct ocfs2_alloc_context *ac) | 545 | struct ocfs2_alloc_context *ac) |
537 | { | 546 | { |
diff --git a/fs/ocfs2/suballoc.h b/fs/ocfs2/suballoc.h index 9e026c8afee4..028fd633b44e 100644 --- a/fs/ocfs2/suballoc.h +++ b/fs/ocfs2/suballoc.h | |||
@@ -67,6 +67,9 @@ static inline int ocfs2_alloc_context_bits_left(struct ocfs2_alloc_context *ac) | |||
67 | int ocfs2_reserve_new_metadata(struct ocfs2_super *osb, | 67 | int ocfs2_reserve_new_metadata(struct ocfs2_super *osb, |
68 | struct ocfs2_extent_list *root_el, | 68 | struct ocfs2_extent_list *root_el, |
69 | struct ocfs2_alloc_context **ac); | 69 | struct ocfs2_alloc_context **ac); |
70 | int ocfs2_reserve_new_metadata_blocks(struct ocfs2_super *osb, | ||
71 | int blocks, | ||
72 | struct ocfs2_alloc_context **ac); | ||
70 | int ocfs2_reserve_new_inode(struct ocfs2_super *osb, | 73 | int ocfs2_reserve_new_inode(struct ocfs2_super *osb, |
71 | struct ocfs2_alloc_context **ac); | 74 | struct ocfs2_alloc_context **ac); |
72 | int ocfs2_reserve_clusters(struct ocfs2_super *osb, | 75 | int ocfs2_reserve_clusters(struct ocfs2_super *osb, |
diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index 9bdb3aeefe89..3b04f5d2e896 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c | |||
@@ -64,6 +64,7 @@ | |||
64 | #include "sysfile.h" | 64 | #include "sysfile.h" |
65 | #include "uptodate.h" | 65 | #include "uptodate.h" |
66 | #include "ver.h" | 66 | #include "ver.h" |
67 | #include "xattr.h" | ||
67 | 68 | ||
68 | #include "buffer_head_io.h" | 69 | #include "buffer_head_io.h" |
69 | 70 | ||
@@ -154,6 +155,8 @@ enum { | |||
154 | Opt_localalloc, | 155 | Opt_localalloc, |
155 | Opt_localflocks, | 156 | Opt_localflocks, |
156 | Opt_stack, | 157 | Opt_stack, |
158 | Opt_user_xattr, | ||
159 | Opt_nouser_xattr, | ||
157 | Opt_err, | 160 | Opt_err, |
158 | }; | 161 | }; |
159 | 162 | ||
@@ -173,6 +176,8 @@ static const match_table_t tokens = { | |||
173 | {Opt_localalloc, "localalloc=%d"}, | 176 | {Opt_localalloc, "localalloc=%d"}, |
174 | {Opt_localflocks, "localflocks"}, | 177 | {Opt_localflocks, "localflocks"}, |
175 | {Opt_stack, "cluster_stack=%s"}, | 178 | {Opt_stack, "cluster_stack=%s"}, |
179 | {Opt_user_xattr, "user_xattr"}, | ||
180 | {Opt_nouser_xattr, "nouser_xattr"}, | ||
176 | {Opt_err, NULL} | 181 | {Opt_err, NULL} |
177 | }; | 182 | }; |
178 | 183 | ||
@@ -848,6 +853,12 @@ static int ocfs2_parse_options(struct super_block *sb, | |||
848 | case Opt_data_writeback: | 853 | case Opt_data_writeback: |
849 | mopt->mount_opt |= OCFS2_MOUNT_DATA_WRITEBACK; | 854 | mopt->mount_opt |= OCFS2_MOUNT_DATA_WRITEBACK; |
850 | break; | 855 | break; |
856 | case Opt_user_xattr: | ||
857 | mopt->mount_opt &= ~OCFS2_MOUNT_NOUSERXATTR; | ||
858 | break; | ||
859 | case Opt_nouser_xattr: | ||
860 | mopt->mount_opt |= OCFS2_MOUNT_NOUSERXATTR; | ||
861 | break; | ||
851 | case Opt_atime_quantum: | 862 | case Opt_atime_quantum: |
852 | if (match_int(&args[0], &option)) { | 863 | if (match_int(&args[0], &option)) { |
853 | status = 0; | 864 | status = 0; |
@@ -1135,6 +1146,7 @@ static void ocfs2_inode_init_once(void *data) | |||
1135 | oi->ip_dir_start_lookup = 0; | 1146 | oi->ip_dir_start_lookup = 0; |
1136 | 1147 | ||
1137 | init_rwsem(&oi->ip_alloc_sem); | 1148 | init_rwsem(&oi->ip_alloc_sem); |
1149 | init_rwsem(&oi->ip_xattr_sem); | ||
1138 | mutex_init(&oi->ip_io_mutex); | 1150 | mutex_init(&oi->ip_io_mutex); |
1139 | 1151 | ||
1140 | oi->ip_blkno = 0ULL; | 1152 | oi->ip_blkno = 0ULL; |
@@ -1378,6 +1390,7 @@ static int ocfs2_initialize_super(struct super_block *sb, | |||
1378 | sb->s_fs_info = osb; | 1390 | sb->s_fs_info = osb; |
1379 | sb->s_op = &ocfs2_sops; | 1391 | sb->s_op = &ocfs2_sops; |
1380 | sb->s_export_op = &ocfs2_export_ops; | 1392 | sb->s_export_op = &ocfs2_export_ops; |
1393 | sb->s_xattr = ocfs2_xattr_handlers; | ||
1381 | sb->s_time_gran = 1; | 1394 | sb->s_time_gran = 1; |
1382 | sb->s_flags |= MS_NOATIME; | 1395 | sb->s_flags |= MS_NOATIME; |
1383 | /* this is needed to support O_LARGEFILE */ | 1396 | /* this is needed to support O_LARGEFILE */ |
@@ -1574,6 +1587,7 @@ static int ocfs2_initialize_super(struct super_block *sb, | |||
1574 | osb->first_cluster_group_blkno = | 1587 | osb->first_cluster_group_blkno = |
1575 | le64_to_cpu(di->id2.i_super.s_first_cluster_group); | 1588 | le64_to_cpu(di->id2.i_super.s_first_cluster_group); |
1576 | osb->fs_generation = le32_to_cpu(di->i_fs_generation); | 1589 | osb->fs_generation = le32_to_cpu(di->i_fs_generation); |
1590 | osb->uuid_hash = le32_to_cpu(di->id2.i_super.s_uuid_hash); | ||
1577 | mlog(0, "vol_label: %s\n", osb->vol_label); | 1591 | mlog(0, "vol_label: %s\n", osb->vol_label); |
1578 | mlog(0, "uuid: %s\n", osb->uuid_str); | 1592 | mlog(0, "uuid: %s\n", osb->uuid_str); |
1579 | mlog(0, "root_blkno=%llu, system_dir_blkno=%llu\n", | 1593 | mlog(0, "root_blkno=%llu, system_dir_blkno=%llu\n", |
diff --git a/fs/ocfs2/symlink.c b/fs/ocfs2/symlink.c index ba9dbb51d25b..8c5879c7f846 100644 --- a/fs/ocfs2/symlink.c +++ b/fs/ocfs2/symlink.c | |||
@@ -50,6 +50,7 @@ | |||
50 | #include "inode.h" | 50 | #include "inode.h" |
51 | #include "journal.h" | 51 | #include "journal.h" |
52 | #include "symlink.h" | 52 | #include "symlink.h" |
53 | #include "xattr.h" | ||
53 | 54 | ||
54 | #include "buffer_head_io.h" | 55 | #include "buffer_head_io.h" |
55 | 56 | ||
@@ -168,10 +169,18 @@ const struct inode_operations ocfs2_symlink_inode_operations = { | |||
168 | .follow_link = ocfs2_follow_link, | 169 | .follow_link = ocfs2_follow_link, |
169 | .getattr = ocfs2_getattr, | 170 | .getattr = ocfs2_getattr, |
170 | .setattr = ocfs2_setattr, | 171 | .setattr = ocfs2_setattr, |
172 | .setxattr = generic_setxattr, | ||
173 | .getxattr = generic_getxattr, | ||
174 | .listxattr = ocfs2_listxattr, | ||
175 | .removexattr = generic_removexattr, | ||
171 | }; | 176 | }; |
172 | const struct inode_operations ocfs2_fast_symlink_inode_operations = { | 177 | const struct inode_operations ocfs2_fast_symlink_inode_operations = { |
173 | .readlink = ocfs2_readlink, | 178 | .readlink = ocfs2_readlink, |
174 | .follow_link = ocfs2_follow_link, | 179 | .follow_link = ocfs2_follow_link, |
175 | .getattr = ocfs2_getattr, | 180 | .getattr = ocfs2_getattr, |
176 | .setattr = ocfs2_setattr, | 181 | .setattr = ocfs2_setattr, |
182 | .setxattr = generic_setxattr, | ||
183 | .getxattr = generic_getxattr, | ||
184 | .listxattr = ocfs2_listxattr, | ||
185 | .removexattr = generic_removexattr, | ||
177 | }; | 186 | }; |
diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index 9604a4cd02bb..67bebd9259e7 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c | |||
@@ -5,6 +5,9 @@ | |||
5 | * | 5 | * |
6 | * Copyright (C) 2008 Oracle. All rights reserved. | 6 | * Copyright (C) 2008 Oracle. All rights reserved. |
7 | * | 7 | * |
8 | * CREDITS: | ||
9 | * Lots of code in this file is taken from ext3. | ||
10 | * | ||
8 | * This program is free software; you can redistribute it and/or | 11 | * This program is free software; you can redistribute it and/or |
9 | * modify it under the terms of the GNU General Public | 12 | * modify it under the terms of the GNU General Public |
10 | * License as published by the Free Software Foundation; either | 13 | * License as published by the Free Software Foundation; either |
@@ -21,6 +24,19 @@ | |||
21 | * Boston, MA 021110-1307, USA. | 24 | * Boston, MA 021110-1307, USA. |
22 | */ | 25 | */ |
23 | 26 | ||
27 | #include <linux/capability.h> | ||
28 | #include <linux/fs.h> | ||
29 | #include <linux/types.h> | ||
30 | #include <linux/slab.h> | ||
31 | #include <linux/highmem.h> | ||
32 | #include <linux/pagemap.h> | ||
33 | #include <linux/uio.h> | ||
34 | #include <linux/sched.h> | ||
35 | #include <linux/splice.h> | ||
36 | #include <linux/mount.h> | ||
37 | #include <linux/writeback.h> | ||
38 | #include <linux/falloc.h> | ||
39 | |||
24 | #define MLOG_MASK_PREFIX ML_XATTR | 40 | #define MLOG_MASK_PREFIX ML_XATTR |
25 | #include <cluster/masklog.h> | 41 | #include <cluster/masklog.h> |
26 | 42 | ||
@@ -28,12 +44,119 @@ | |||
28 | #include "alloc.h" | 44 | #include "alloc.h" |
29 | #include "dlmglue.h" | 45 | #include "dlmglue.h" |
30 | #include "file.h" | 46 | #include "file.h" |
47 | #include "symlink.h" | ||
48 | #include "sysfile.h" | ||
31 | #include "inode.h" | 49 | #include "inode.h" |
32 | #include "journal.h" | 50 | #include "journal.h" |
33 | #include "ocfs2_fs.h" | 51 | #include "ocfs2_fs.h" |
34 | #include "suballoc.h" | 52 | #include "suballoc.h" |
35 | #include "uptodate.h" | 53 | #include "uptodate.h" |
36 | #include "buffer_head_io.h" | 54 | #include "buffer_head_io.h" |
55 | #include "xattr.h" | ||
56 | |||
57 | |||
58 | struct ocfs2_xattr_def_value_root { | ||
59 | struct ocfs2_xattr_value_root xv; | ||
60 | struct ocfs2_extent_rec er; | ||
61 | }; | ||
62 | |||
63 | #define OCFS2_XATTR_ROOT_SIZE (sizeof(struct ocfs2_xattr_def_value_root)) | ||
64 | #define OCFS2_XATTR_INLINE_SIZE 80 | ||
65 | |||
66 | static struct ocfs2_xattr_def_value_root def_xv = { | ||
67 | .xv.xr_list.l_count = cpu_to_le16(1), | ||
68 | }; | ||
69 | |||
70 | struct xattr_handler *ocfs2_xattr_handlers[] = { | ||
71 | &ocfs2_xattr_user_handler, | ||
72 | &ocfs2_xattr_trusted_handler, | ||
73 | NULL | ||
74 | }; | ||
75 | |||
76 | static struct xattr_handler *ocfs2_xattr_handler_map[] = { | ||
77 | [OCFS2_XATTR_INDEX_USER] = &ocfs2_xattr_user_handler, | ||
78 | [OCFS2_XATTR_INDEX_TRUSTED] = &ocfs2_xattr_trusted_handler, | ||
79 | }; | ||
80 | |||
81 | struct ocfs2_xattr_info { | ||
82 | int name_index; | ||
83 | const char *name; | ||
84 | const void *value; | ||
85 | size_t value_len; | ||
86 | }; | ||
87 | |||
88 | struct ocfs2_xattr_search { | ||
89 | struct buffer_head *inode_bh; | ||
90 | /* | ||
91 | * xattr_bh point to the block buffer head which has extended attribute | ||
92 | * when extended attribute in inode, xattr_bh is equal to inode_bh. | ||
93 | */ | ||
94 | struct buffer_head *xattr_bh; | ||
95 | struct ocfs2_xattr_header *header; | ||
96 | void *base; | ||
97 | void *end; | ||
98 | struct ocfs2_xattr_entry *here; | ||
99 | int not_found; | ||
100 | }; | ||
101 | |||
102 | static inline struct xattr_handler *ocfs2_xattr_handler(int name_index) | ||
103 | { | ||
104 | struct xattr_handler *handler = NULL; | ||
105 | |||
106 | if (name_index > 0 && name_index < OCFS2_XATTR_MAX) | ||
107 | handler = ocfs2_xattr_handler_map[name_index]; | ||
108 | |||
109 | return handler; | ||
110 | } | ||
111 | |||
112 | static inline u32 ocfs2_xattr_name_hash(struct inode *inode, | ||
113 | char *prefix, | ||
114 | int prefix_len, | ||
115 | char *name, | ||
116 | int name_len) | ||
117 | { | ||
118 | /* Get hash value of uuid from super block */ | ||
119 | u32 hash = OCFS2_SB(inode->i_sb)->uuid_hash; | ||
120 | int i; | ||
121 | |||
122 | /* hash extended attribute prefix */ | ||
123 | for (i = 0; i < prefix_len; i++) { | ||
124 | hash = (hash << OCFS2_HASH_SHIFT) ^ | ||
125 | (hash >> (8*sizeof(hash) - OCFS2_HASH_SHIFT)) ^ | ||
126 | *prefix++; | ||
127 | } | ||
128 | /* hash extended attribute name */ | ||
129 | for (i = 0; i < name_len; i++) { | ||
130 | hash = (hash << OCFS2_HASH_SHIFT) ^ | ||
131 | (hash >> (8*sizeof(hash) - OCFS2_HASH_SHIFT)) ^ | ||
132 | *name++; | ||
133 | } | ||
134 | |||
135 | return hash; | ||
136 | } | ||
137 | |||
138 | /* | ||
139 | * ocfs2_xattr_hash_entry() | ||
140 | * | ||
141 | * Compute the hash of an extended attribute. | ||
142 | */ | ||
143 | static void ocfs2_xattr_hash_entry(struct inode *inode, | ||
144 | struct ocfs2_xattr_header *header, | ||
145 | struct ocfs2_xattr_entry *entry) | ||
146 | { | ||
147 | u32 hash = 0; | ||
148 | struct xattr_handler *handler = | ||
149 | ocfs2_xattr_handler(ocfs2_xattr_get_type(entry)); | ||
150 | char *prefix = handler->prefix; | ||
151 | char *name = (char *)header + le16_to_cpu(entry->xe_name_offset); | ||
152 | int prefix_len = strlen(handler->prefix); | ||
153 | |||
154 | hash = ocfs2_xattr_name_hash(inode, prefix, prefix_len, name, | ||
155 | entry->xe_name_len); | ||
156 | entry->xe_name_hash = cpu_to_le32(hash); | ||
157 | |||
158 | return; | ||
159 | } | ||
37 | 160 | ||
38 | static int ocfs2_xattr_extend_allocation(struct inode *inode, | 161 | static int ocfs2_xattr_extend_allocation(struct inode *inode, |
39 | u32 clusters_to_add, | 162 | u32 clusters_to_add, |
@@ -303,3 +426,1500 @@ static int ocfs2_xattr_value_truncate(struct inode *inode, | |||
303 | 426 | ||
304 | return ret; | 427 | return ret; |
305 | } | 428 | } |
429 | |||
430 | static int ocfs2_xattr_list_entries(struct inode *inode, | ||
431 | struct ocfs2_xattr_header *header, | ||
432 | char *buffer, size_t buffer_size) | ||
433 | { | ||
434 | size_t rest = buffer_size; | ||
435 | int i; | ||
436 | |||
437 | for (i = 0 ; i < le16_to_cpu(header->xh_count); i++) { | ||
438 | struct ocfs2_xattr_entry *entry = &header->xh_entries[i]; | ||
439 | struct xattr_handler *handler = | ||
440 | ocfs2_xattr_handler(ocfs2_xattr_get_type(entry)); | ||
441 | |||
442 | if (handler) { | ||
443 | size_t size = handler->list(inode, buffer, rest, | ||
444 | ((char *)header + | ||
445 | le16_to_cpu(entry->xe_name_offset)), | ||
446 | entry->xe_name_len); | ||
447 | if (buffer) { | ||
448 | if (size > rest) | ||
449 | return -ERANGE; | ||
450 | buffer += size; | ||
451 | } | ||
452 | rest -= size; | ||
453 | } | ||
454 | } | ||
455 | |||
456 | return buffer_size - rest; | ||
457 | } | ||
458 | |||
459 | static int ocfs2_xattr_ibody_list(struct inode *inode, | ||
460 | struct ocfs2_dinode *di, | ||
461 | char *buffer, | ||
462 | size_t buffer_size) | ||
463 | { | ||
464 | struct ocfs2_xattr_header *header = NULL; | ||
465 | struct ocfs2_inode_info *oi = OCFS2_I(inode); | ||
466 | int ret = 0; | ||
467 | |||
468 | if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)) | ||
469 | return ret; | ||
470 | |||
471 | header = (struct ocfs2_xattr_header *) | ||
472 | ((void *)di + inode->i_sb->s_blocksize - | ||
473 | le16_to_cpu(di->i_xattr_inline_size)); | ||
474 | |||
475 | ret = ocfs2_xattr_list_entries(inode, header, buffer, buffer_size); | ||
476 | |||
477 | return ret; | ||
478 | } | ||
479 | |||
480 | static int ocfs2_xattr_block_list(struct inode *inode, | ||
481 | struct ocfs2_dinode *di, | ||
482 | char *buffer, | ||
483 | size_t buffer_size) | ||
484 | { | ||
485 | struct buffer_head *blk_bh = NULL; | ||
486 | struct ocfs2_xattr_header *header = NULL; | ||
487 | int ret = 0; | ||
488 | |||
489 | if (!di->i_xattr_loc) | ||
490 | return ret; | ||
491 | |||
492 | ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), | ||
493 | le64_to_cpu(di->i_xattr_loc), | ||
494 | &blk_bh, OCFS2_BH_CACHED, inode); | ||
495 | if (ret < 0) { | ||
496 | mlog_errno(ret); | ||
497 | return ret; | ||
498 | } | ||
499 | /*Verify the signature of xattr block*/ | ||
500 | if (memcmp((void *)blk_bh->b_data, OCFS2_XATTR_BLOCK_SIGNATURE, | ||
501 | strlen(OCFS2_XATTR_BLOCK_SIGNATURE))) { | ||
502 | ret = -EFAULT; | ||
503 | goto cleanup; | ||
504 | } | ||
505 | |||
506 | header = &((struct ocfs2_xattr_block *)blk_bh->b_data)-> | ||
507 | xb_attrs.xb_header; | ||
508 | |||
509 | ret = ocfs2_xattr_list_entries(inode, header, buffer, buffer_size); | ||
510 | cleanup: | ||
511 | brelse(blk_bh); | ||
512 | |||
513 | return ret; | ||
514 | } | ||
515 | |||
516 | ssize_t ocfs2_listxattr(struct dentry *dentry, | ||
517 | char *buffer, | ||
518 | size_t size) | ||
519 | { | ||
520 | int ret = 0, i_ret = 0, b_ret = 0; | ||
521 | struct buffer_head *di_bh = NULL; | ||
522 | struct ocfs2_dinode *di = NULL; | ||
523 | struct ocfs2_inode_info *oi = OCFS2_I(dentry->d_inode); | ||
524 | |||
525 | if (!(oi->ip_dyn_features & OCFS2_HAS_XATTR_FL)) | ||
526 | return ret; | ||
527 | |||
528 | ret = ocfs2_inode_lock(dentry->d_inode, &di_bh, 0); | ||
529 | if (ret < 0) { | ||
530 | mlog_errno(ret); | ||
531 | return ret; | ||
532 | } | ||
533 | |||
534 | di = (struct ocfs2_dinode *)di_bh->b_data; | ||
535 | |||
536 | down_read(&oi->ip_xattr_sem); | ||
537 | i_ret = ocfs2_xattr_ibody_list(dentry->d_inode, di, buffer, size); | ||
538 | if (i_ret < 0) | ||
539 | b_ret = 0; | ||
540 | else { | ||
541 | if (buffer) { | ||
542 | buffer += i_ret; | ||
543 | size -= i_ret; | ||
544 | } | ||
545 | b_ret = ocfs2_xattr_block_list(dentry->d_inode, di, | ||
546 | buffer, size); | ||
547 | if (b_ret < 0) | ||
548 | i_ret = 0; | ||
549 | } | ||
550 | up_read(&oi->ip_xattr_sem); | ||
551 | ocfs2_inode_unlock(dentry->d_inode, 0); | ||
552 | |||
553 | brelse(di_bh); | ||
554 | |||
555 | return i_ret + b_ret; | ||
556 | } | ||
557 | |||
558 | static int ocfs2_xattr_find_entry(int name_index, | ||
559 | const char *name, | ||
560 | struct ocfs2_xattr_search *xs) | ||
561 | { | ||
562 | struct ocfs2_xattr_entry *entry; | ||
563 | size_t name_len; | ||
564 | int i, cmp = 1; | ||
565 | |||
566 | if (name == NULL) | ||
567 | return -EINVAL; | ||
568 | |||
569 | name_len = strlen(name); | ||
570 | entry = xs->here; | ||
571 | for (i = 0; i < le16_to_cpu(xs->header->xh_count); i++) { | ||
572 | cmp = name_index - ocfs2_xattr_get_type(entry); | ||
573 | if (!cmp) | ||
574 | cmp = name_len - entry->xe_name_len; | ||
575 | if (!cmp) | ||
576 | cmp = memcmp(name, (xs->base + | ||
577 | le16_to_cpu(entry->xe_name_offset)), | ||
578 | name_len); | ||
579 | if (cmp == 0) | ||
580 | break; | ||
581 | entry += 1; | ||
582 | } | ||
583 | xs->here = entry; | ||
584 | |||
585 | return cmp ? -ENODATA : 0; | ||
586 | } | ||
587 | |||
588 | static int ocfs2_xattr_get_value_outside(struct inode *inode, | ||
589 | struct ocfs2_xattr_search *xs, | ||
590 | void *buffer, | ||
591 | size_t len) | ||
592 | { | ||
593 | u32 cpos, p_cluster, num_clusters, bpc, clusters; | ||
594 | u64 blkno; | ||
595 | int i, ret = 0; | ||
596 | size_t cplen, blocksize; | ||
597 | struct buffer_head *bh = NULL; | ||
598 | struct ocfs2_xattr_value_root *xv; | ||
599 | struct ocfs2_extent_list *el; | ||
600 | |||
601 | xv = (struct ocfs2_xattr_value_root *) | ||
602 | (xs->base + le16_to_cpu(xs->here->xe_name_offset) + | ||
603 | OCFS2_XATTR_SIZE(xs->here->xe_name_len)); | ||
604 | el = &xv->xr_list; | ||
605 | clusters = le32_to_cpu(xv->xr_clusters); | ||
606 | bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1); | ||
607 | blocksize = inode->i_sb->s_blocksize; | ||
608 | |||
609 | cpos = 0; | ||
610 | while (cpos < clusters) { | ||
611 | ret = ocfs2_xattr_get_clusters(inode, cpos, &p_cluster, | ||
612 | &num_clusters, el); | ||
613 | if (ret) { | ||
614 | mlog_errno(ret); | ||
615 | goto out; | ||
616 | } | ||
617 | |||
618 | blkno = ocfs2_clusters_to_blocks(inode->i_sb, p_cluster); | ||
619 | /* Copy ocfs2_xattr_value */ | ||
620 | for (i = 0; i < num_clusters * bpc; i++, blkno++) { | ||
621 | ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), blkno, | ||
622 | &bh, OCFS2_BH_CACHED, inode); | ||
623 | if (ret) { | ||
624 | mlog_errno(ret); | ||
625 | goto out; | ||
626 | } | ||
627 | |||
628 | cplen = len >= blocksize ? blocksize : len; | ||
629 | memcpy(buffer, bh->b_data, cplen); | ||
630 | len -= cplen; | ||
631 | buffer += cplen; | ||
632 | |||
633 | brelse(bh); | ||
634 | bh = NULL; | ||
635 | if (len == 0) | ||
636 | break; | ||
637 | } | ||
638 | cpos += num_clusters; | ||
639 | } | ||
640 | out: | ||
641 | return ret; | ||
642 | } | ||
643 | |||
644 | static int ocfs2_xattr_ibody_get(struct inode *inode, | ||
645 | int name_index, | ||
646 | const char *name, | ||
647 | void *buffer, | ||
648 | size_t buffer_size, | ||
649 | struct ocfs2_xattr_search *xs) | ||
650 | { | ||
651 | struct ocfs2_inode_info *oi = OCFS2_I(inode); | ||
652 | struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data; | ||
653 | size_t size; | ||
654 | int ret = 0; | ||
655 | |||
656 | if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)) | ||
657 | return -ENODATA; | ||
658 | |||
659 | xs->end = (void *)di + inode->i_sb->s_blocksize; | ||
660 | xs->header = (struct ocfs2_xattr_header *) | ||
661 | (xs->end - le16_to_cpu(di->i_xattr_inline_size)); | ||
662 | xs->base = (void *)xs->header; | ||
663 | xs->here = xs->header->xh_entries; | ||
664 | |||
665 | ret = ocfs2_xattr_find_entry(name_index, name, xs); | ||
666 | if (ret) | ||
667 | return ret; | ||
668 | size = le64_to_cpu(xs->here->xe_value_size); | ||
669 | if (buffer) { | ||
670 | if (size > buffer_size) | ||
671 | return -ERANGE; | ||
672 | if (ocfs2_xattr_is_local(xs->here)) { | ||
673 | memcpy(buffer, (void *)xs->base + | ||
674 | le16_to_cpu(xs->here->xe_name_offset) + | ||
675 | OCFS2_XATTR_SIZE(xs->here->xe_name_len), size); | ||
676 | } else { | ||
677 | ret = ocfs2_xattr_get_value_outside(inode, xs, | ||
678 | buffer, size); | ||
679 | if (ret < 0) { | ||
680 | mlog_errno(ret); | ||
681 | return ret; | ||
682 | } | ||
683 | } | ||
684 | } | ||
685 | |||
686 | return size; | ||
687 | } | ||
688 | |||
689 | static int ocfs2_xattr_block_get(struct inode *inode, | ||
690 | int name_index, | ||
691 | const char *name, | ||
692 | void *buffer, | ||
693 | size_t buffer_size, | ||
694 | struct ocfs2_xattr_search *xs) | ||
695 | { | ||
696 | struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data; | ||
697 | struct buffer_head *blk_bh = NULL; | ||
698 | struct ocfs2_xattr_block *xb; | ||
699 | size_t size; | ||
700 | int ret = -ENODATA; | ||
701 | |||
702 | if (!di->i_xattr_loc) | ||
703 | return ret; | ||
704 | |||
705 | ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), | ||
706 | le64_to_cpu(di->i_xattr_loc), | ||
707 | &blk_bh, OCFS2_BH_CACHED, inode); | ||
708 | if (ret < 0) { | ||
709 | mlog_errno(ret); | ||
710 | return ret; | ||
711 | } | ||
712 | /*Verify the signature of xattr block*/ | ||
713 | if (memcmp((void *)blk_bh->b_data, OCFS2_XATTR_BLOCK_SIGNATURE, | ||
714 | strlen(OCFS2_XATTR_BLOCK_SIGNATURE))) { | ||
715 | ret = -EFAULT; | ||
716 | goto cleanup; | ||
717 | } | ||
718 | |||
719 | xs->xattr_bh = blk_bh; | ||
720 | xb = (struct ocfs2_xattr_block *)blk_bh->b_data; | ||
721 | xs->header = &xb->xb_attrs.xb_header; | ||
722 | xs->base = (void *)xs->header; | ||
723 | xs->end = (void *)(blk_bh->b_data) + blk_bh->b_size; | ||
724 | xs->here = xs->header->xh_entries; | ||
725 | |||
726 | ret = ocfs2_xattr_find_entry(name_index, name, xs); | ||
727 | if (ret) | ||
728 | goto cleanup; | ||
729 | size = le64_to_cpu(xs->here->xe_value_size); | ||
730 | if (buffer) { | ||
731 | ret = -ERANGE; | ||
732 | if (size > buffer_size) | ||
733 | goto cleanup; | ||
734 | if (ocfs2_xattr_is_local(xs->here)) { | ||
735 | memcpy(buffer, (void *)xs->base + | ||
736 | le16_to_cpu(xs->here->xe_name_offset) + | ||
737 | OCFS2_XATTR_SIZE(xs->here->xe_name_len), size); | ||
738 | } else { | ||
739 | ret = ocfs2_xattr_get_value_outside(inode, xs, | ||
740 | buffer, size); | ||
741 | if (ret < 0) { | ||
742 | mlog_errno(ret); | ||
743 | goto cleanup; | ||
744 | } | ||
745 | } | ||
746 | } | ||
747 | ret = size; | ||
748 | cleanup: | ||
749 | brelse(blk_bh); | ||
750 | |||
751 | return ret; | ||
752 | } | ||
753 | |||
754 | /* ocfs2_xattr_get() | ||
755 | * | ||
756 | * Copy an extended attribute into the buffer provided. | ||
757 | * Buffer is NULL to compute the size of buffer required. | ||
758 | */ | ||
759 | int ocfs2_xattr_get(struct inode *inode, | ||
760 | int name_index, | ||
761 | const char *name, | ||
762 | void *buffer, | ||
763 | size_t buffer_size) | ||
764 | { | ||
765 | int ret; | ||
766 | struct ocfs2_dinode *di = NULL; | ||
767 | struct buffer_head *di_bh = NULL; | ||
768 | struct ocfs2_inode_info *oi = OCFS2_I(inode); | ||
769 | struct ocfs2_xattr_search xis = { | ||
770 | .not_found = -ENODATA, | ||
771 | }; | ||
772 | struct ocfs2_xattr_search xbs = { | ||
773 | .not_found = -ENODATA, | ||
774 | }; | ||
775 | |||
776 | if (!(oi->ip_dyn_features & OCFS2_HAS_XATTR_FL)) | ||
777 | ret = -ENODATA; | ||
778 | |||
779 | ret = ocfs2_inode_lock(inode, &di_bh, 0); | ||
780 | if (ret < 0) { | ||
781 | mlog_errno(ret); | ||
782 | return ret; | ||
783 | } | ||
784 | xis.inode_bh = xbs.inode_bh = di_bh; | ||
785 | di = (struct ocfs2_dinode *)di_bh->b_data; | ||
786 | |||
787 | down_read(&oi->ip_xattr_sem); | ||
788 | ret = ocfs2_xattr_ibody_get(inode, name_index, name, buffer, | ||
789 | buffer_size, &xis); | ||
790 | if (ret == -ENODATA) | ||
791 | ret = ocfs2_xattr_block_get(inode, name_index, name, buffer, | ||
792 | buffer_size, &xbs); | ||
793 | up_read(&oi->ip_xattr_sem); | ||
794 | ocfs2_inode_unlock(inode, 0); | ||
795 | |||
796 | brelse(di_bh); | ||
797 | |||
798 | return ret; | ||
799 | } | ||
800 | |||
801 | static int __ocfs2_xattr_set_value_outside(struct inode *inode, | ||
802 | struct ocfs2_xattr_value_root *xv, | ||
803 | const void *value, | ||
804 | int value_len) | ||
805 | { | ||
806 | int ret = 0, i, cp_len, credits; | ||
807 | u16 blocksize = inode->i_sb->s_blocksize; | ||
808 | u32 p_cluster, num_clusters; | ||
809 | u32 cpos = 0, bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1); | ||
810 | u32 clusters = ocfs2_clusters_for_bytes(inode->i_sb, value_len); | ||
811 | u64 blkno; | ||
812 | struct buffer_head *bh = NULL; | ||
813 | handle_t *handle; | ||
814 | |||
815 | BUG_ON(clusters > le32_to_cpu(xv->xr_clusters)); | ||
816 | |||
817 | credits = clusters * bpc; | ||
818 | handle = ocfs2_start_trans(OCFS2_SB(inode->i_sb), credits); | ||
819 | if (IS_ERR(handle)) { | ||
820 | ret = PTR_ERR(handle); | ||
821 | mlog_errno(ret); | ||
822 | goto out; | ||
823 | } | ||
824 | |||
825 | while (cpos < clusters) { | ||
826 | ret = ocfs2_xattr_get_clusters(inode, cpos, &p_cluster, | ||
827 | &num_clusters, &xv->xr_list); | ||
828 | if (ret) { | ||
829 | mlog_errno(ret); | ||
830 | goto out_commit; | ||
831 | } | ||
832 | |||
833 | blkno = ocfs2_clusters_to_blocks(inode->i_sb, p_cluster); | ||
834 | |||
835 | for (i = 0; i < num_clusters * bpc; i++, blkno++) { | ||
836 | ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), blkno, | ||
837 | &bh, OCFS2_BH_CACHED, inode); | ||
838 | if (ret) { | ||
839 | mlog_errno(ret); | ||
840 | goto out_commit; | ||
841 | } | ||
842 | |||
843 | ret = ocfs2_journal_access(handle, | ||
844 | inode, | ||
845 | bh, | ||
846 | OCFS2_JOURNAL_ACCESS_WRITE); | ||
847 | if (ret < 0) { | ||
848 | mlog_errno(ret); | ||
849 | goto out_commit; | ||
850 | } | ||
851 | |||
852 | cp_len = value_len > blocksize ? blocksize : value_len; | ||
853 | memcpy(bh->b_data, value, cp_len); | ||
854 | value_len -= cp_len; | ||
855 | value += cp_len; | ||
856 | if (cp_len < blocksize) | ||
857 | memset(bh->b_data + cp_len, 0, | ||
858 | blocksize - cp_len); | ||
859 | |||
860 | ret = ocfs2_journal_dirty(handle, bh); | ||
861 | if (ret < 0) { | ||
862 | mlog_errno(ret); | ||
863 | goto out_commit; | ||
864 | } | ||
865 | brelse(bh); | ||
866 | bh = NULL; | ||
867 | |||
868 | /* | ||
869 | * XXX: do we need to empty all the following | ||
870 | * blocks in this cluster? | ||
871 | */ | ||
872 | if (!value_len) | ||
873 | break; | ||
874 | } | ||
875 | cpos += num_clusters; | ||
876 | } | ||
877 | out_commit: | ||
878 | ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle); | ||
879 | out: | ||
880 | brelse(bh); | ||
881 | |||
882 | return ret; | ||
883 | } | ||
884 | |||
885 | static int ocfs2_xattr_cleanup(struct inode *inode, | ||
886 | struct ocfs2_xattr_info *xi, | ||
887 | struct ocfs2_xattr_search *xs, | ||
888 | size_t offs) | ||
889 | { | ||
890 | handle_t *handle = NULL; | ||
891 | int ret = 0; | ||
892 | size_t name_len = strlen(xi->name); | ||
893 | void *val = xs->base + offs; | ||
894 | size_t size = OCFS2_XATTR_SIZE(name_len) + OCFS2_XATTR_ROOT_SIZE; | ||
895 | |||
896 | handle = ocfs2_start_trans((OCFS2_SB(inode->i_sb)), | ||
897 | OCFS2_XATTR_BLOCK_UPDATE_CREDITS); | ||
898 | if (IS_ERR(handle)) { | ||
899 | ret = PTR_ERR(handle); | ||
900 | mlog_errno(ret); | ||
901 | goto out; | ||
902 | } | ||
903 | ret = ocfs2_journal_access(handle, inode, xs->xattr_bh, | ||
904 | OCFS2_JOURNAL_ACCESS_WRITE); | ||
905 | if (ret) { | ||
906 | mlog_errno(ret); | ||
907 | goto out_commit; | ||
908 | } | ||
909 | /* Decrease xattr count */ | ||
910 | le16_add_cpu(&xs->header->xh_count, -1); | ||
911 | /* Remove the xattr entry and tree root which has already be set*/ | ||
912 | memset((void *)xs->here, 0, sizeof(struct ocfs2_xattr_entry)); | ||
913 | memset(val, 0, size); | ||
914 | |||
915 | ret = ocfs2_journal_dirty(handle, xs->xattr_bh); | ||
916 | if (ret < 0) | ||
917 | mlog_errno(ret); | ||
918 | out_commit: | ||
919 | ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle); | ||
920 | out: | ||
921 | return ret; | ||
922 | } | ||
923 | |||
924 | static int ocfs2_xattr_update_entry(struct inode *inode, | ||
925 | struct ocfs2_xattr_info *xi, | ||
926 | struct ocfs2_xattr_search *xs, | ||
927 | size_t offs) | ||
928 | { | ||
929 | handle_t *handle = NULL; | ||
930 | int ret = 0; | ||
931 | |||
932 | handle = ocfs2_start_trans((OCFS2_SB(inode->i_sb)), | ||
933 | OCFS2_XATTR_BLOCK_UPDATE_CREDITS); | ||
934 | if (IS_ERR(handle)) { | ||
935 | ret = PTR_ERR(handle); | ||
936 | mlog_errno(ret); | ||
937 | goto out; | ||
938 | } | ||
939 | ret = ocfs2_journal_access(handle, inode, xs->xattr_bh, | ||
940 | OCFS2_JOURNAL_ACCESS_WRITE); | ||
941 | if (ret) { | ||
942 | mlog_errno(ret); | ||
943 | goto out_commit; | ||
944 | } | ||
945 | |||
946 | xs->here->xe_name_offset = cpu_to_le16(offs); | ||
947 | xs->here->xe_value_size = cpu_to_le64(xi->value_len); | ||
948 | if (xi->value_len <= OCFS2_XATTR_INLINE_SIZE) | ||
949 | ocfs2_xattr_set_local(xs->here, 1); | ||
950 | else | ||
951 | ocfs2_xattr_set_local(xs->here, 0); | ||
952 | ocfs2_xattr_hash_entry(inode, xs->header, xs->here); | ||
953 | |||
954 | ret = ocfs2_journal_dirty(handle, xs->xattr_bh); | ||
955 | if (ret < 0) | ||
956 | mlog_errno(ret); | ||
957 | out_commit: | ||
958 | ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle); | ||
959 | out: | ||
960 | return ret; | ||
961 | } | ||
962 | |||
963 | /* | ||
964 | * ocfs2_xattr_set_value_outside() | ||
965 | * | ||
966 | * Set large size value in B tree. | ||
967 | */ | ||
968 | static int ocfs2_xattr_set_value_outside(struct inode *inode, | ||
969 | struct ocfs2_xattr_info *xi, | ||
970 | struct ocfs2_xattr_search *xs, | ||
971 | size_t offs) | ||
972 | { | ||
973 | size_t name_len = strlen(xi->name); | ||
974 | void *val = xs->base + offs; | ||
975 | struct ocfs2_xattr_value_root *xv = NULL; | ||
976 | size_t size = OCFS2_XATTR_SIZE(name_len) + OCFS2_XATTR_ROOT_SIZE; | ||
977 | int ret = 0; | ||
978 | |||
979 | memset(val, 0, size); | ||
980 | memcpy(val, xi->name, name_len); | ||
981 | xv = (struct ocfs2_xattr_value_root *) | ||
982 | (val + OCFS2_XATTR_SIZE(name_len)); | ||
983 | xv->xr_clusters = 0; | ||
984 | xv->xr_last_eb_blk = 0; | ||
985 | xv->xr_list.l_tree_depth = 0; | ||
986 | xv->xr_list.l_count = cpu_to_le16(1); | ||
987 | xv->xr_list.l_next_free_rec = 0; | ||
988 | |||
989 | ret = ocfs2_xattr_value_truncate(inode, xs->xattr_bh, xv, | ||
990 | xi->value_len); | ||
991 | if (ret < 0) { | ||
992 | mlog_errno(ret); | ||
993 | return ret; | ||
994 | } | ||
995 | ret = __ocfs2_xattr_set_value_outside(inode, xv, xi->value, | ||
996 | xi->value_len); | ||
997 | if (ret < 0) { | ||
998 | mlog_errno(ret); | ||
999 | return ret; | ||
1000 | } | ||
1001 | ret = ocfs2_xattr_update_entry(inode, xi, xs, offs); | ||
1002 | if (ret < 0) | ||
1003 | mlog_errno(ret); | ||
1004 | |||
1005 | return ret; | ||
1006 | } | ||
1007 | |||
1008 | /* | ||
1009 | * ocfs2_xattr_set_entry_local() | ||
1010 | * | ||
1011 | * Set, replace or remove extended attribute in local. | ||
1012 | */ | ||
1013 | static void ocfs2_xattr_set_entry_local(struct inode *inode, | ||
1014 | struct ocfs2_xattr_info *xi, | ||
1015 | struct ocfs2_xattr_search *xs, | ||
1016 | struct ocfs2_xattr_entry *last, | ||
1017 | size_t min_offs) | ||
1018 | { | ||
1019 | size_t name_len = strlen(xi->name); | ||
1020 | int i; | ||
1021 | |||
1022 | if (xi->value && xs->not_found) { | ||
1023 | /* Insert the new xattr entry. */ | ||
1024 | le16_add_cpu(&xs->header->xh_count, 1); | ||
1025 | ocfs2_xattr_set_type(last, xi->name_index); | ||
1026 | ocfs2_xattr_set_local(last, 1); | ||
1027 | last->xe_name_len = name_len; | ||
1028 | } else { | ||
1029 | void *first_val; | ||
1030 | void *val; | ||
1031 | size_t offs, size; | ||
1032 | |||
1033 | first_val = xs->base + min_offs; | ||
1034 | offs = le16_to_cpu(xs->here->xe_name_offset); | ||
1035 | val = xs->base + offs; | ||
1036 | |||
1037 | if (le64_to_cpu(xs->here->xe_value_size) > | ||
1038 | OCFS2_XATTR_INLINE_SIZE) | ||
1039 | size = OCFS2_XATTR_SIZE(name_len) + | ||
1040 | OCFS2_XATTR_ROOT_SIZE; | ||
1041 | else | ||
1042 | size = OCFS2_XATTR_SIZE(name_len) + | ||
1043 | OCFS2_XATTR_SIZE(le64_to_cpu(xs->here->xe_value_size)); | ||
1044 | |||
1045 | if (xi->value && size == OCFS2_XATTR_SIZE(name_len) + | ||
1046 | OCFS2_XATTR_SIZE(xi->value_len)) { | ||
1047 | /* The old and the new value have the | ||
1048 | same size. Just replace the value. */ | ||
1049 | ocfs2_xattr_set_local(xs->here, 1); | ||
1050 | xs->here->xe_value_size = cpu_to_le64(xi->value_len); | ||
1051 | /* Clear value bytes. */ | ||
1052 | memset(val + OCFS2_XATTR_SIZE(name_len), | ||
1053 | 0, | ||
1054 | OCFS2_XATTR_SIZE(xi->value_len)); | ||
1055 | memcpy(val + OCFS2_XATTR_SIZE(name_len), | ||
1056 | xi->value, | ||
1057 | xi->value_len); | ||
1058 | return; | ||
1059 | } | ||
1060 | /* Remove the old name+value. */ | ||
1061 | memmove(first_val + size, first_val, val - first_val); | ||
1062 | memset(first_val, 0, size); | ||
1063 | xs->here->xe_name_hash = 0; | ||
1064 | xs->here->xe_name_offset = 0; | ||
1065 | ocfs2_xattr_set_local(xs->here, 1); | ||
1066 | xs->here->xe_value_size = 0; | ||
1067 | |||
1068 | min_offs += size; | ||
1069 | |||
1070 | /* Adjust all value offsets. */ | ||
1071 | last = xs->header->xh_entries; | ||
1072 | for (i = 0 ; i < le16_to_cpu(xs->header->xh_count); i++) { | ||
1073 | size_t o = le16_to_cpu(last->xe_name_offset); | ||
1074 | |||
1075 | if (o < offs) | ||
1076 | last->xe_name_offset = cpu_to_le16(o + size); | ||
1077 | last += 1; | ||
1078 | } | ||
1079 | |||
1080 | if (!xi->value) { | ||
1081 | /* Remove the old entry. */ | ||
1082 | last -= 1; | ||
1083 | memmove(xs->here, xs->here + 1, | ||
1084 | (void *)last - (void *)xs->here); | ||
1085 | memset(last, 0, sizeof(struct ocfs2_xattr_entry)); | ||
1086 | le16_add_cpu(&xs->header->xh_count, -1); | ||
1087 | } | ||
1088 | } | ||
1089 | if (xi->value) { | ||
1090 | /* Insert the new name+value. */ | ||
1091 | size_t size = OCFS2_XATTR_SIZE(name_len) + | ||
1092 | OCFS2_XATTR_SIZE(xi->value_len); | ||
1093 | void *val = xs->base + min_offs - size; | ||
1094 | |||
1095 | xs->here->xe_name_offset = cpu_to_le16(min_offs - size); | ||
1096 | memset(val, 0, size); | ||
1097 | memcpy(val, xi->name, name_len); | ||
1098 | memcpy(val + OCFS2_XATTR_SIZE(name_len), | ||
1099 | xi->value, | ||
1100 | xi->value_len); | ||
1101 | xs->here->xe_value_size = cpu_to_le64(xi->value_len); | ||
1102 | ocfs2_xattr_set_local(xs->here, 1); | ||
1103 | ocfs2_xattr_hash_entry(inode, xs->header, xs->here); | ||
1104 | } | ||
1105 | |||
1106 | return; | ||
1107 | } | ||
1108 | |||
1109 | /* | ||
1110 | * ocfs2_xattr_set_entry() | ||
1111 | * | ||
1112 | * Set extended attribute entry into inode or block. | ||
1113 | * | ||
1114 | * If extended attribute value size > OCFS2_XATTR_INLINE_SIZE, | ||
1115 | * We first insert tree root(ocfs2_xattr_value_root) with set_entry_local(), | ||
1116 | * then set value in B tree with set_value_outside(). | ||
1117 | */ | ||
1118 | static int ocfs2_xattr_set_entry(struct inode *inode, | ||
1119 | struct ocfs2_xattr_info *xi, | ||
1120 | struct ocfs2_xattr_search *xs, | ||
1121 | int flag) | ||
1122 | { | ||
1123 | struct ocfs2_xattr_entry *last; | ||
1124 | struct ocfs2_inode_info *oi = OCFS2_I(inode); | ||
1125 | struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data; | ||
1126 | size_t min_offs = xs->end - xs->base, name_len = strlen(xi->name); | ||
1127 | size_t size_l = 0; | ||
1128 | handle_t *handle = NULL; | ||
1129 | int free, i, ret; | ||
1130 | struct ocfs2_xattr_info xi_l = { | ||
1131 | .name_index = xi->name_index, | ||
1132 | .name = xi->name, | ||
1133 | .value = xi->value, | ||
1134 | .value_len = xi->value_len, | ||
1135 | }; | ||
1136 | |||
1137 | /* Compute min_offs, last and free space. */ | ||
1138 | last = xs->header->xh_entries; | ||
1139 | |||
1140 | for (i = 0 ; i < le16_to_cpu(xs->header->xh_count); i++) { | ||
1141 | size_t offs = le16_to_cpu(last->xe_name_offset); | ||
1142 | if (offs < min_offs) | ||
1143 | min_offs = offs; | ||
1144 | last += 1; | ||
1145 | } | ||
1146 | |||
1147 | free = min_offs - ((void *)last - xs->base) - sizeof(__u32); | ||
1148 | if (free < 0) | ||
1149 | return -EFAULT; | ||
1150 | |||
1151 | if (!xs->not_found) { | ||
1152 | size_t size = 0; | ||
1153 | if (ocfs2_xattr_is_local(xs->here)) | ||
1154 | size = OCFS2_XATTR_SIZE(name_len) + | ||
1155 | OCFS2_XATTR_SIZE(le64_to_cpu(xs->here->xe_value_size)); | ||
1156 | else | ||
1157 | size = OCFS2_XATTR_SIZE(name_len) + | ||
1158 | OCFS2_XATTR_ROOT_SIZE; | ||
1159 | free += (size + sizeof(struct ocfs2_xattr_entry)); | ||
1160 | } | ||
1161 | /* Check free space in inode or block */ | ||
1162 | if (xi->value && xi->value_len > OCFS2_XATTR_INLINE_SIZE) { | ||
1163 | if (free < sizeof(struct ocfs2_xattr_entry) + | ||
1164 | OCFS2_XATTR_SIZE(name_len) + | ||
1165 | OCFS2_XATTR_ROOT_SIZE) { | ||
1166 | ret = -ENOSPC; | ||
1167 | goto out; | ||
1168 | } | ||
1169 | size_l = OCFS2_XATTR_SIZE(name_len) + OCFS2_XATTR_ROOT_SIZE; | ||
1170 | xi_l.value = (void *)&def_xv; | ||
1171 | xi_l.value_len = OCFS2_XATTR_ROOT_SIZE; | ||
1172 | } else if (xi->value) { | ||
1173 | if (free < sizeof(struct ocfs2_xattr_entry) + | ||
1174 | OCFS2_XATTR_SIZE(name_len) + | ||
1175 | OCFS2_XATTR_SIZE(xi->value_len)) { | ||
1176 | ret = -ENOSPC; | ||
1177 | goto out; | ||
1178 | } | ||
1179 | } | ||
1180 | |||
1181 | if (!xs->not_found) { | ||
1182 | /* For existing extended attribute */ | ||
1183 | size_t size = OCFS2_XATTR_SIZE(name_len) + | ||
1184 | OCFS2_XATTR_SIZE(le64_to_cpu(xs->here->xe_value_size)); | ||
1185 | size_t offs = le16_to_cpu(xs->here->xe_name_offset); | ||
1186 | void *val = xs->base + offs; | ||
1187 | |||
1188 | if (ocfs2_xattr_is_local(xs->here) && size == size_l) { | ||
1189 | /* Replace existing local xattr with tree root */ | ||
1190 | ret = ocfs2_xattr_set_value_outside(inode, xi, xs, | ||
1191 | offs); | ||
1192 | if (ret < 0) | ||
1193 | mlog_errno(ret); | ||
1194 | goto out; | ||
1195 | } else if (!ocfs2_xattr_is_local(xs->here)) { | ||
1196 | /* For existing xattr which has value outside */ | ||
1197 | struct ocfs2_xattr_value_root *xv = NULL; | ||
1198 | xv = (struct ocfs2_xattr_value_root *)(val + | ||
1199 | OCFS2_XATTR_SIZE(name_len)); | ||
1200 | |||
1201 | if (xi->value_len > OCFS2_XATTR_INLINE_SIZE) { | ||
1202 | /* | ||
1203 | * If new value need set outside also, | ||
1204 | * first truncate old value to new value, | ||
1205 | * then set new value with set_value_outside(). | ||
1206 | */ | ||
1207 | ret = ocfs2_xattr_value_truncate(inode, | ||
1208 | xs->xattr_bh, | ||
1209 | xv, | ||
1210 | xi->value_len); | ||
1211 | if (ret < 0) { | ||
1212 | mlog_errno(ret); | ||
1213 | goto out; | ||
1214 | } | ||
1215 | |||
1216 | ret = __ocfs2_xattr_set_value_outside(inode, | ||
1217 | xv, | ||
1218 | xi->value, | ||
1219 | xi->value_len); | ||
1220 | if (ret < 0) { | ||
1221 | mlog_errno(ret); | ||
1222 | goto out; | ||
1223 | } | ||
1224 | |||
1225 | ret = ocfs2_xattr_update_entry(inode, | ||
1226 | xi, | ||
1227 | xs, | ||
1228 | offs); | ||
1229 | if (ret < 0) | ||
1230 | mlog_errno(ret); | ||
1231 | goto out; | ||
1232 | } else { | ||
1233 | /* | ||
1234 | * If new value need set in local, | ||
1235 | * just trucate old value to zero. | ||
1236 | */ | ||
1237 | ret = ocfs2_xattr_value_truncate(inode, | ||
1238 | xs->xattr_bh, | ||
1239 | xv, | ||
1240 | 0); | ||
1241 | if (ret < 0) | ||
1242 | mlog_errno(ret); | ||
1243 | } | ||
1244 | } | ||
1245 | } | ||
1246 | |||
1247 | handle = ocfs2_start_trans((OCFS2_SB(inode->i_sb)), | ||
1248 | OCFS2_INODE_UPDATE_CREDITS); | ||
1249 | if (IS_ERR(handle)) { | ||
1250 | ret = PTR_ERR(handle); | ||
1251 | mlog_errno(ret); | ||
1252 | goto out; | ||
1253 | } | ||
1254 | |||
1255 | ret = ocfs2_journal_access(handle, inode, xs->inode_bh, | ||
1256 | OCFS2_JOURNAL_ACCESS_WRITE); | ||
1257 | if (ret) { | ||
1258 | mlog_errno(ret); | ||
1259 | goto out_commit; | ||
1260 | } | ||
1261 | |||
1262 | if (!(flag & OCFS2_INLINE_XATTR_FL)) { | ||
1263 | /*set extended attribue in external blcok*/ | ||
1264 | ret = ocfs2_extend_trans(handle, | ||
1265 | OCFS2_XATTR_BLOCK_UPDATE_CREDITS); | ||
1266 | if (ret) { | ||
1267 | mlog_errno(ret); | ||
1268 | goto out_commit; | ||
1269 | } | ||
1270 | ret = ocfs2_journal_access(handle, inode, xs->xattr_bh, | ||
1271 | OCFS2_JOURNAL_ACCESS_WRITE); | ||
1272 | if (ret) { | ||
1273 | mlog_errno(ret); | ||
1274 | goto out_commit; | ||
1275 | } | ||
1276 | } | ||
1277 | |||
1278 | /* | ||
1279 | * Set value in local, include set tree root in local. | ||
1280 | * This is the first step for value size >INLINE_SIZE. | ||
1281 | */ | ||
1282 | ocfs2_xattr_set_entry_local(inode, &xi_l, xs, last, min_offs); | ||
1283 | |||
1284 | if (!(flag & OCFS2_INLINE_XATTR_FL)) { | ||
1285 | ret = ocfs2_journal_dirty(handle, xs->xattr_bh); | ||
1286 | if (ret < 0) { | ||
1287 | mlog_errno(ret); | ||
1288 | goto out_commit; | ||
1289 | } | ||
1290 | } | ||
1291 | |||
1292 | if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL) && | ||
1293 | (flag & OCFS2_INLINE_XATTR_FL)) { | ||
1294 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | ||
1295 | unsigned int xattrsize = osb->s_xattr_inline_size; | ||
1296 | |||
1297 | /* | ||
1298 | * Adjust extent record count or inline data size | ||
1299 | * to reserve space for extended attribute. | ||
1300 | */ | ||
1301 | if (oi->ip_dyn_features & OCFS2_INLINE_DATA_FL) { | ||
1302 | struct ocfs2_inline_data *idata = &di->id2.i_data; | ||
1303 | le16_add_cpu(&idata->id_count, -xattrsize); | ||
1304 | } else if (!(ocfs2_inode_is_fast_symlink(inode))) { | ||
1305 | struct ocfs2_extent_list *el = &di->id2.i_list; | ||
1306 | le16_add_cpu(&el->l_count, -(xattrsize / | ||
1307 | sizeof(struct ocfs2_extent_rec))); | ||
1308 | } | ||
1309 | di->i_xattr_inline_size = cpu_to_le16(xattrsize); | ||
1310 | } | ||
1311 | /* Update xattr flag */ | ||
1312 | spin_lock(&oi->ip_lock); | ||
1313 | oi->ip_dyn_features |= flag; | ||
1314 | di->i_dyn_features = cpu_to_le16(oi->ip_dyn_features); | ||
1315 | spin_unlock(&oi->ip_lock); | ||
1316 | /* Update inode ctime */ | ||
1317 | inode->i_ctime = CURRENT_TIME; | ||
1318 | di->i_ctime = cpu_to_le64(inode->i_ctime.tv_sec); | ||
1319 | di->i_ctime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec); | ||
1320 | |||
1321 | ret = ocfs2_journal_dirty(handle, xs->inode_bh); | ||
1322 | if (ret < 0) | ||
1323 | mlog_errno(ret); | ||
1324 | |||
1325 | out_commit: | ||
1326 | ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle); | ||
1327 | |||
1328 | if (!ret && xi->value_len > OCFS2_XATTR_INLINE_SIZE) { | ||
1329 | /* | ||
1330 | * Set value outside in B tree. | ||
1331 | * This is the second step for value size > INLINE_SIZE. | ||
1332 | */ | ||
1333 | size_t offs = le16_to_cpu(xs->here->xe_name_offset); | ||
1334 | ret = ocfs2_xattr_set_value_outside(inode, xi, xs, offs); | ||
1335 | if (ret < 0) { | ||
1336 | int ret2; | ||
1337 | |||
1338 | mlog_errno(ret); | ||
1339 | /* | ||
1340 | * If set value outside failed, we have to clean | ||
1341 | * the junk tree root we have already set in local. | ||
1342 | */ | ||
1343 | ret2 = ocfs2_xattr_cleanup(inode, xi, xs, offs); | ||
1344 | if (ret2 < 0) | ||
1345 | mlog_errno(ret2); | ||
1346 | } | ||
1347 | } | ||
1348 | out: | ||
1349 | return ret; | ||
1350 | |||
1351 | } | ||
1352 | |||
1353 | static int ocfs2_xattr_free_block(handle_t *handle, | ||
1354 | struct ocfs2_super *osb, | ||
1355 | struct ocfs2_xattr_block *xb) | ||
1356 | { | ||
1357 | struct inode *xb_alloc_inode; | ||
1358 | struct buffer_head *xb_alloc_bh = NULL; | ||
1359 | u64 blk = le64_to_cpu(xb->xb_blkno); | ||
1360 | u16 bit = le16_to_cpu(xb->xb_suballoc_bit); | ||
1361 | u64 bg_blkno = ocfs2_which_suballoc_group(blk, bit); | ||
1362 | int ret = 0; | ||
1363 | |||
1364 | xb_alloc_inode = ocfs2_get_system_file_inode(osb, | ||
1365 | EXTENT_ALLOC_SYSTEM_INODE, | ||
1366 | le16_to_cpu(xb->xb_suballoc_slot)); | ||
1367 | if (!xb_alloc_inode) { | ||
1368 | ret = -ENOMEM; | ||
1369 | mlog_errno(ret); | ||
1370 | goto out; | ||
1371 | } | ||
1372 | mutex_lock(&xb_alloc_inode->i_mutex); | ||
1373 | |||
1374 | ret = ocfs2_inode_lock(xb_alloc_inode, &xb_alloc_bh, 1); | ||
1375 | if (ret < 0) { | ||
1376 | mlog_errno(ret); | ||
1377 | goto out_mutex; | ||
1378 | } | ||
1379 | ret = ocfs2_extend_trans(handle, OCFS2_SUBALLOC_FREE); | ||
1380 | if (ret < 0) { | ||
1381 | mlog_errno(ret); | ||
1382 | goto out_unlock; | ||
1383 | } | ||
1384 | ret = ocfs2_free_suballoc_bits(handle, xb_alloc_inode, xb_alloc_bh, | ||
1385 | bit, bg_blkno, 1); | ||
1386 | if (ret < 0) | ||
1387 | mlog_errno(ret); | ||
1388 | out_unlock: | ||
1389 | ocfs2_inode_unlock(xb_alloc_inode, 1); | ||
1390 | brelse(xb_alloc_bh); | ||
1391 | out_mutex: | ||
1392 | mutex_unlock(&xb_alloc_inode->i_mutex); | ||
1393 | iput(xb_alloc_inode); | ||
1394 | out: | ||
1395 | return ret; | ||
1396 | } | ||
1397 | |||
1398 | static int ocfs2_remove_value_outside(struct inode*inode, | ||
1399 | struct buffer_head *bh, | ||
1400 | struct ocfs2_xattr_header *header) | ||
1401 | { | ||
1402 | int ret = 0, i; | ||
1403 | |||
1404 | for (i = 0; i < le16_to_cpu(header->xh_count); i++) { | ||
1405 | struct ocfs2_xattr_entry *entry = &header->xh_entries[i]; | ||
1406 | |||
1407 | if (!ocfs2_xattr_is_local(entry)) { | ||
1408 | struct ocfs2_xattr_value_root *xv; | ||
1409 | void *val; | ||
1410 | |||
1411 | val = (void *)header + | ||
1412 | le16_to_cpu(entry->xe_name_offset); | ||
1413 | xv = (struct ocfs2_xattr_value_root *) | ||
1414 | (val + OCFS2_XATTR_SIZE(entry->xe_name_len)); | ||
1415 | ret = ocfs2_xattr_value_truncate(inode, bh, xv, 0); | ||
1416 | if (ret < 0) { | ||
1417 | mlog_errno(ret); | ||
1418 | return ret; | ||
1419 | } | ||
1420 | } | ||
1421 | } | ||
1422 | |||
1423 | return ret; | ||
1424 | } | ||
1425 | |||
1426 | static int ocfs2_xattr_ibody_remove(struct inode *inode, | ||
1427 | struct buffer_head *di_bh) | ||
1428 | { | ||
1429 | |||
1430 | struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; | ||
1431 | struct ocfs2_xattr_header *header; | ||
1432 | int ret; | ||
1433 | |||
1434 | header = (struct ocfs2_xattr_header *) | ||
1435 | ((void *)di + inode->i_sb->s_blocksize - | ||
1436 | le16_to_cpu(di->i_xattr_inline_size)); | ||
1437 | |||
1438 | ret = ocfs2_remove_value_outside(inode, di_bh, header); | ||
1439 | |||
1440 | return ret; | ||
1441 | } | ||
1442 | |||
1443 | static int ocfs2_xattr_block_remove(struct inode *inode, | ||
1444 | struct buffer_head *blk_bh) | ||
1445 | { | ||
1446 | struct ocfs2_xattr_block *xb; | ||
1447 | struct ocfs2_xattr_header *header; | ||
1448 | int ret = 0; | ||
1449 | |||
1450 | xb = (struct ocfs2_xattr_block *)blk_bh->b_data; | ||
1451 | header = &(xb->xb_attrs.xb_header); | ||
1452 | |||
1453 | ret = ocfs2_remove_value_outside(inode, blk_bh, header); | ||
1454 | |||
1455 | return ret; | ||
1456 | } | ||
1457 | |||
1458 | /* | ||
1459 | * ocfs2_xattr_remove() | ||
1460 | * | ||
1461 | * Free extended attribute resources associated with this inode. | ||
1462 | */ | ||
1463 | int ocfs2_xattr_remove(struct inode *inode, struct buffer_head *di_bh) | ||
1464 | { | ||
1465 | struct ocfs2_xattr_block *xb; | ||
1466 | struct buffer_head *blk_bh = NULL; | ||
1467 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | ||
1468 | struct ocfs2_inode_info *oi = OCFS2_I(inode); | ||
1469 | struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; | ||
1470 | handle_t *handle; | ||
1471 | int ret; | ||
1472 | |||
1473 | if (!(oi->ip_dyn_features & OCFS2_HAS_XATTR_FL)) | ||
1474 | return 0; | ||
1475 | |||
1476 | if (oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL) { | ||
1477 | ret = ocfs2_xattr_ibody_remove(inode, di_bh); | ||
1478 | if (ret < 0) { | ||
1479 | mlog_errno(ret); | ||
1480 | goto out; | ||
1481 | } | ||
1482 | } | ||
1483 | if (di->i_xattr_loc) { | ||
1484 | ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), | ||
1485 | le64_to_cpu(di->i_xattr_loc), | ||
1486 | &blk_bh, OCFS2_BH_CACHED, inode); | ||
1487 | if (ret < 0) { | ||
1488 | mlog_errno(ret); | ||
1489 | return ret; | ||
1490 | } | ||
1491 | /*Verify the signature of xattr block*/ | ||
1492 | if (memcmp((void *)blk_bh->b_data, OCFS2_XATTR_BLOCK_SIGNATURE, | ||
1493 | strlen(OCFS2_XATTR_BLOCK_SIGNATURE))) { | ||
1494 | ret = -EFAULT; | ||
1495 | goto out; | ||
1496 | } | ||
1497 | |||
1498 | ret = ocfs2_xattr_block_remove(inode, blk_bh); | ||
1499 | if (ret < 0) { | ||
1500 | mlog_errno(ret); | ||
1501 | goto out; | ||
1502 | } | ||
1503 | } | ||
1504 | |||
1505 | handle = ocfs2_start_trans((OCFS2_SB(inode->i_sb)), | ||
1506 | OCFS2_INODE_UPDATE_CREDITS); | ||
1507 | if (IS_ERR(handle)) { | ||
1508 | ret = PTR_ERR(handle); | ||
1509 | mlog_errno(ret); | ||
1510 | goto out; | ||
1511 | } | ||
1512 | ret = ocfs2_journal_access(handle, inode, di_bh, | ||
1513 | OCFS2_JOURNAL_ACCESS_WRITE); | ||
1514 | if (ret) { | ||
1515 | mlog_errno(ret); | ||
1516 | goto out_commit; | ||
1517 | } | ||
1518 | |||
1519 | if (di->i_xattr_loc) { | ||
1520 | xb = (struct ocfs2_xattr_block *)blk_bh->b_data; | ||
1521 | ocfs2_xattr_free_block(handle, osb, xb); | ||
1522 | di->i_xattr_loc = cpu_to_le64(0); | ||
1523 | } | ||
1524 | |||
1525 | spin_lock(&oi->ip_lock); | ||
1526 | oi->ip_dyn_features &= ~(OCFS2_INLINE_XATTR_FL | OCFS2_HAS_XATTR_FL); | ||
1527 | di->i_dyn_features = cpu_to_le16(oi->ip_dyn_features); | ||
1528 | spin_unlock(&oi->ip_lock); | ||
1529 | |||
1530 | ret = ocfs2_journal_dirty(handle, di_bh); | ||
1531 | if (ret < 0) | ||
1532 | mlog_errno(ret); | ||
1533 | out_commit: | ||
1534 | ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle); | ||
1535 | out: | ||
1536 | brelse(blk_bh); | ||
1537 | |||
1538 | return ret; | ||
1539 | } | ||
1540 | |||
1541 | static int ocfs2_xattr_has_space_inline(struct inode *inode, | ||
1542 | struct ocfs2_dinode *di) | ||
1543 | { | ||
1544 | struct ocfs2_inode_info *oi = OCFS2_I(inode); | ||
1545 | unsigned int xattrsize = OCFS2_SB(inode->i_sb)->s_xattr_inline_size; | ||
1546 | int free; | ||
1547 | |||
1548 | if (xattrsize < OCFS2_MIN_XATTR_INLINE_SIZE) | ||
1549 | return 0; | ||
1550 | |||
1551 | if (oi->ip_dyn_features & OCFS2_INLINE_DATA_FL) { | ||
1552 | struct ocfs2_inline_data *idata = &di->id2.i_data; | ||
1553 | free = le16_to_cpu(idata->id_count) - le64_to_cpu(di->i_size); | ||
1554 | } else if (ocfs2_inode_is_fast_symlink(inode)) { | ||
1555 | free = ocfs2_fast_symlink_chars(inode->i_sb) - | ||
1556 | le64_to_cpu(di->i_size); | ||
1557 | } else { | ||
1558 | struct ocfs2_extent_list *el = &di->id2.i_list; | ||
1559 | free = (le16_to_cpu(el->l_count) - | ||
1560 | le16_to_cpu(el->l_next_free_rec)) * | ||
1561 | sizeof(struct ocfs2_extent_rec); | ||
1562 | } | ||
1563 | if (free >= xattrsize) | ||
1564 | return 1; | ||
1565 | |||
1566 | return 0; | ||
1567 | } | ||
1568 | |||
1569 | /* | ||
1570 | * ocfs2_xattr_ibody_find() | ||
1571 | * | ||
1572 | * Find extended attribute in inode block and | ||
1573 | * fill search info into struct ocfs2_xattr_search. | ||
1574 | */ | ||
1575 | static int ocfs2_xattr_ibody_find(struct inode *inode, | ||
1576 | int name_index, | ||
1577 | const char *name, | ||
1578 | struct ocfs2_xattr_search *xs) | ||
1579 | { | ||
1580 | struct ocfs2_inode_info *oi = OCFS2_I(inode); | ||
1581 | struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data; | ||
1582 | int ret; | ||
1583 | int has_space = 0; | ||
1584 | |||
1585 | if (inode->i_sb->s_blocksize == OCFS2_MIN_BLOCKSIZE) | ||
1586 | return 0; | ||
1587 | |||
1588 | if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)) { | ||
1589 | down_read(&oi->ip_alloc_sem); | ||
1590 | has_space = ocfs2_xattr_has_space_inline(inode, di); | ||
1591 | up_read(&oi->ip_alloc_sem); | ||
1592 | if (!has_space) | ||
1593 | return 0; | ||
1594 | } | ||
1595 | |||
1596 | xs->xattr_bh = xs->inode_bh; | ||
1597 | xs->end = (void *)di + inode->i_sb->s_blocksize; | ||
1598 | if (oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL) | ||
1599 | xs->header = (struct ocfs2_xattr_header *) | ||
1600 | (xs->end - le16_to_cpu(di->i_xattr_inline_size)); | ||
1601 | else | ||
1602 | xs->header = (struct ocfs2_xattr_header *) | ||
1603 | (xs->end - OCFS2_SB(inode->i_sb)->s_xattr_inline_size); | ||
1604 | xs->base = (void *)xs->header; | ||
1605 | xs->here = xs->header->xh_entries; | ||
1606 | |||
1607 | /* Find the named attribute. */ | ||
1608 | if (oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL) { | ||
1609 | ret = ocfs2_xattr_find_entry(name_index, name, xs); | ||
1610 | if (ret && ret != -ENODATA) | ||
1611 | return ret; | ||
1612 | xs->not_found = ret; | ||
1613 | } | ||
1614 | |||
1615 | return 0; | ||
1616 | } | ||
1617 | |||
1618 | /* | ||
1619 | * ocfs2_xattr_ibody_set() | ||
1620 | * | ||
1621 | * Set, replace or remove an extended attribute into inode block. | ||
1622 | * | ||
1623 | */ | ||
1624 | static int ocfs2_xattr_ibody_set(struct inode *inode, | ||
1625 | struct ocfs2_xattr_info *xi, | ||
1626 | struct ocfs2_xattr_search *xs) | ||
1627 | { | ||
1628 | struct ocfs2_inode_info *oi = OCFS2_I(inode); | ||
1629 | struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data; | ||
1630 | int ret; | ||
1631 | |||
1632 | if (inode->i_sb->s_blocksize == OCFS2_MIN_BLOCKSIZE) | ||
1633 | return -ENOSPC; | ||
1634 | |||
1635 | down_write(&oi->ip_alloc_sem); | ||
1636 | if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)) { | ||
1637 | if (!ocfs2_xattr_has_space_inline(inode, di)) { | ||
1638 | ret = -ENOSPC; | ||
1639 | goto out; | ||
1640 | } | ||
1641 | } | ||
1642 | |||
1643 | ret = ocfs2_xattr_set_entry(inode, xi, xs, | ||
1644 | (OCFS2_INLINE_XATTR_FL | OCFS2_HAS_XATTR_FL)); | ||
1645 | out: | ||
1646 | up_write(&oi->ip_alloc_sem); | ||
1647 | |||
1648 | return ret; | ||
1649 | } | ||
1650 | |||
1651 | /* | ||
1652 | * ocfs2_xattr_block_find() | ||
1653 | * | ||
1654 | * Find extended attribute in external block and | ||
1655 | * fill search info into struct ocfs2_xattr_search. | ||
1656 | */ | ||
1657 | static int ocfs2_xattr_block_find(struct inode *inode, | ||
1658 | int name_index, | ||
1659 | const char *name, | ||
1660 | struct ocfs2_xattr_search *xs) | ||
1661 | { | ||
1662 | struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data; | ||
1663 | struct buffer_head *blk_bh = NULL; | ||
1664 | int ret = 0; | ||
1665 | |||
1666 | if (!di->i_xattr_loc) | ||
1667 | return ret; | ||
1668 | |||
1669 | ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), | ||
1670 | le64_to_cpu(di->i_xattr_loc), | ||
1671 | &blk_bh, OCFS2_BH_CACHED, inode); | ||
1672 | if (ret < 0) { | ||
1673 | mlog_errno(ret); | ||
1674 | return ret; | ||
1675 | } | ||
1676 | /*Verify the signature of xattr block*/ | ||
1677 | if (memcmp((void *)blk_bh->b_data, OCFS2_XATTR_BLOCK_SIGNATURE, | ||
1678 | strlen(OCFS2_XATTR_BLOCK_SIGNATURE))) { | ||
1679 | ret = -EFAULT; | ||
1680 | goto cleanup; | ||
1681 | } | ||
1682 | |||
1683 | xs->xattr_bh = blk_bh; | ||
1684 | xs->header = &((struct ocfs2_xattr_block *)blk_bh->b_data)-> | ||
1685 | xb_attrs.xb_header; | ||
1686 | xs->base = (void *)xs->header; | ||
1687 | xs->end = (void *)(blk_bh->b_data) + blk_bh->b_size; | ||
1688 | xs->here = xs->header->xh_entries; | ||
1689 | |||
1690 | ret = ocfs2_xattr_find_entry(name_index, name, xs); | ||
1691 | if (ret && ret != -ENODATA) { | ||
1692 | xs->xattr_bh = NULL; | ||
1693 | goto cleanup; | ||
1694 | } | ||
1695 | xs->not_found = ret; | ||
1696 | return 0; | ||
1697 | |||
1698 | cleanup: | ||
1699 | brelse(blk_bh); | ||
1700 | |||
1701 | return ret; | ||
1702 | } | ||
1703 | |||
1704 | /* | ||
1705 | * ocfs2_xattr_block_set() | ||
1706 | * | ||
1707 | * Set, replace or remove an extended attribute into external block. | ||
1708 | * | ||
1709 | */ | ||
1710 | static int ocfs2_xattr_block_set(struct inode *inode, | ||
1711 | struct ocfs2_xattr_info *xi, | ||
1712 | struct ocfs2_xattr_search *xs) | ||
1713 | { | ||
1714 | struct buffer_head *new_bh = NULL; | ||
1715 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | ||
1716 | struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data; | ||
1717 | struct ocfs2_alloc_context *meta_ac = NULL; | ||
1718 | handle_t *handle = NULL; | ||
1719 | struct ocfs2_xattr_block *xblk = NULL; | ||
1720 | u16 suballoc_bit_start; | ||
1721 | u32 num_got; | ||
1722 | u64 first_blkno; | ||
1723 | int ret; | ||
1724 | |||
1725 | if (!xs->xattr_bh) { | ||
1726 | /* | ||
1727 | * Alloc one external block for extended attribute | ||
1728 | * outside of inode. | ||
1729 | */ | ||
1730 | ret = ocfs2_reserve_new_metadata_blocks(osb, 1, &meta_ac); | ||
1731 | if (ret < 0) { | ||
1732 | mlog_errno(ret); | ||
1733 | goto out; | ||
1734 | } | ||
1735 | handle = ocfs2_start_trans(osb, | ||
1736 | OCFS2_XATTR_BLOCK_CREATE_CREDITS); | ||
1737 | if (IS_ERR(handle)) { | ||
1738 | ret = PTR_ERR(handle); | ||
1739 | mlog_errno(ret); | ||
1740 | goto out; | ||
1741 | } | ||
1742 | ret = ocfs2_journal_access(handle, inode, xs->inode_bh, | ||
1743 | OCFS2_JOURNAL_ACCESS_CREATE); | ||
1744 | if (ret < 0) { | ||
1745 | mlog_errno(ret); | ||
1746 | goto out_commit; | ||
1747 | } | ||
1748 | |||
1749 | ret = ocfs2_claim_metadata(osb, handle, meta_ac, 1, | ||
1750 | &suballoc_bit_start, &num_got, | ||
1751 | &first_blkno); | ||
1752 | if (ret < 0) { | ||
1753 | mlog_errno(ret); | ||
1754 | goto out_commit; | ||
1755 | } | ||
1756 | |||
1757 | new_bh = sb_getblk(inode->i_sb, first_blkno); | ||
1758 | ocfs2_set_new_buffer_uptodate(inode, new_bh); | ||
1759 | |||
1760 | ret = ocfs2_journal_access(handle, inode, new_bh, | ||
1761 | OCFS2_JOURNAL_ACCESS_CREATE); | ||
1762 | if (ret < 0) { | ||
1763 | mlog_errno(ret); | ||
1764 | goto out_commit; | ||
1765 | } | ||
1766 | |||
1767 | /* Initialize ocfs2_xattr_block */ | ||
1768 | xs->xattr_bh = new_bh; | ||
1769 | xblk = (struct ocfs2_xattr_block *)new_bh->b_data; | ||
1770 | memset(xblk, 0, inode->i_sb->s_blocksize); | ||
1771 | strcpy((void *)xblk, OCFS2_XATTR_BLOCK_SIGNATURE); | ||
1772 | xblk->xb_suballoc_slot = cpu_to_le16(osb->slot_num); | ||
1773 | xblk->xb_suballoc_bit = cpu_to_le16(suballoc_bit_start); | ||
1774 | xblk->xb_fs_generation = cpu_to_le32(osb->fs_generation); | ||
1775 | xblk->xb_blkno = cpu_to_le64(first_blkno); | ||
1776 | |||
1777 | xs->header = &xblk->xb_attrs.xb_header; | ||
1778 | xs->base = (void *)xs->header; | ||
1779 | xs->end = (void *)xblk + inode->i_sb->s_blocksize; | ||
1780 | xs->here = xs->header->xh_entries; | ||
1781 | |||
1782 | |||
1783 | ret = ocfs2_journal_dirty(handle, new_bh); | ||
1784 | if (ret < 0) { | ||
1785 | mlog_errno(ret); | ||
1786 | goto out_commit; | ||
1787 | } | ||
1788 | di->i_xattr_loc = cpu_to_le64(first_blkno); | ||
1789 | ret = ocfs2_journal_dirty(handle, xs->inode_bh); | ||
1790 | if (ret < 0) | ||
1791 | mlog_errno(ret); | ||
1792 | out_commit: | ||
1793 | ocfs2_commit_trans(osb, handle); | ||
1794 | out: | ||
1795 | if (meta_ac) | ||
1796 | ocfs2_free_alloc_context(meta_ac); | ||
1797 | if (ret < 0) | ||
1798 | return ret; | ||
1799 | } | ||
1800 | |||
1801 | /* Set extended attribute into external block */ | ||
1802 | ret = ocfs2_xattr_set_entry(inode, xi, xs, OCFS2_HAS_XATTR_FL); | ||
1803 | |||
1804 | return ret; | ||
1805 | } | ||
1806 | |||
1807 | /* | ||
1808 | * ocfs2_xattr_set() | ||
1809 | * | ||
1810 | * Set, replace or remove an extended attribute for this inode. | ||
1811 | * value is NULL to remove an existing extended attribute, else either | ||
1812 | * create or replace an extended attribute. | ||
1813 | */ | ||
1814 | int ocfs2_xattr_set(struct inode *inode, | ||
1815 | int name_index, | ||
1816 | const char *name, | ||
1817 | const void *value, | ||
1818 | size_t value_len, | ||
1819 | int flags) | ||
1820 | { | ||
1821 | struct buffer_head *di_bh = NULL; | ||
1822 | struct ocfs2_dinode *di; | ||
1823 | int ret; | ||
1824 | |||
1825 | struct ocfs2_xattr_info xi = { | ||
1826 | .name_index = name_index, | ||
1827 | .name = name, | ||
1828 | .value = value, | ||
1829 | .value_len = value_len, | ||
1830 | }; | ||
1831 | |||
1832 | struct ocfs2_xattr_search xis = { | ||
1833 | .not_found = -ENODATA, | ||
1834 | }; | ||
1835 | |||
1836 | struct ocfs2_xattr_search xbs = { | ||
1837 | .not_found = -ENODATA, | ||
1838 | }; | ||
1839 | |||
1840 | ret = ocfs2_inode_lock(inode, &di_bh, 1); | ||
1841 | if (ret < 0) { | ||
1842 | mlog_errno(ret); | ||
1843 | return ret; | ||
1844 | } | ||
1845 | xis.inode_bh = xbs.inode_bh = di_bh; | ||
1846 | di = (struct ocfs2_dinode *)di_bh->b_data; | ||
1847 | |||
1848 | down_write(&OCFS2_I(inode)->ip_xattr_sem); | ||
1849 | /* | ||
1850 | * Scan inode and external block to find the same name | ||
1851 | * extended attribute and collect search infomation. | ||
1852 | */ | ||
1853 | ret = ocfs2_xattr_ibody_find(inode, name_index, name, &xis); | ||
1854 | if (ret) | ||
1855 | goto cleanup; | ||
1856 | if (xis.not_found) { | ||
1857 | ret = ocfs2_xattr_block_find(inode, name_index, name, &xbs); | ||
1858 | if (ret) | ||
1859 | goto cleanup; | ||
1860 | } | ||
1861 | |||
1862 | if (xis.not_found && xbs.not_found) { | ||
1863 | ret = -ENODATA; | ||
1864 | if (flags & XATTR_REPLACE) | ||
1865 | goto cleanup; | ||
1866 | ret = 0; | ||
1867 | if (!value) | ||
1868 | goto cleanup; | ||
1869 | } else { | ||
1870 | ret = -EEXIST; | ||
1871 | if (flags & XATTR_CREATE) | ||
1872 | goto cleanup; | ||
1873 | } | ||
1874 | |||
1875 | if (!value) { | ||
1876 | /* Remove existing extended attribute */ | ||
1877 | if (!xis.not_found) | ||
1878 | ret = ocfs2_xattr_ibody_set(inode, &xi, &xis); | ||
1879 | else if (!xbs.not_found) | ||
1880 | ret = ocfs2_xattr_block_set(inode, &xi, &xbs); | ||
1881 | } else { | ||
1882 | /* We always try to set extended attribute into inode first*/ | ||
1883 | ret = ocfs2_xattr_ibody_set(inode, &xi, &xis); | ||
1884 | if (!ret && !xbs.not_found) { | ||
1885 | /* | ||
1886 | * If succeed and that extended attribute existing in | ||
1887 | * external block, then we will remove it. | ||
1888 | */ | ||
1889 | xi.value = NULL; | ||
1890 | xi.value_len = 0; | ||
1891 | ret = ocfs2_xattr_block_set(inode, &xi, &xbs); | ||
1892 | } else if (ret == -ENOSPC) { | ||
1893 | if (di->i_xattr_loc && !xbs.xattr_bh) { | ||
1894 | ret = ocfs2_xattr_block_find(inode, name_index, | ||
1895 | name, &xbs); | ||
1896 | if (ret) | ||
1897 | goto cleanup; | ||
1898 | } | ||
1899 | /* | ||
1900 | * If no space in inode, we will set extended attribute | ||
1901 | * into external block. | ||
1902 | */ | ||
1903 | ret = ocfs2_xattr_block_set(inode, &xi, &xbs); | ||
1904 | if (ret) | ||
1905 | goto cleanup; | ||
1906 | if (!xis.not_found) { | ||
1907 | /* | ||
1908 | * If succeed and that extended attribute | ||
1909 | * existing in inode, we will remove it. | ||
1910 | */ | ||
1911 | xi.value = NULL; | ||
1912 | xi.value_len = 0; | ||
1913 | ret = ocfs2_xattr_ibody_set(inode, &xi, &xis); | ||
1914 | } | ||
1915 | } | ||
1916 | } | ||
1917 | cleanup: | ||
1918 | up_write(&OCFS2_I(inode)->ip_xattr_sem); | ||
1919 | ocfs2_inode_unlock(inode, 1); | ||
1920 | brelse(di_bh); | ||
1921 | brelse(xbs.xattr_bh); | ||
1922 | |||
1923 | return ret; | ||
1924 | } | ||
1925 | |||
diff --git a/fs/ocfs2/xattr.h b/fs/ocfs2/xattr.h new file mode 100644 index 000000000000..ed32377be9db --- /dev/null +++ b/fs/ocfs2/xattr.h | |||
@@ -0,0 +1,51 @@ | |||
1 | /* -*- mode: c; c-basic-offset: 8; -*- | ||
2 | * vim: noexpandtab sw=8 ts=8 sts=0: | ||
3 | * | ||
4 | * xattr.h | ||
5 | * | ||
6 | * Function prototypes | ||
7 | * | ||
8 | * Copyright (C) 2008 Oracle. All rights reserved. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public | ||
12 | * License as published by the Free Software Foundation; either | ||
13 | * version 2 of the License, or (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public | ||
21 | * License along with this program; if not, write to the | ||
22 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
23 | * Boston, MA 021110-1307, USA. | ||
24 | */ | ||
25 | |||
26 | #ifndef OCFS2_XATTR_H | ||
27 | #define OCFS2_XATTR_H | ||
28 | |||
29 | #include <linux/init.h> | ||
30 | #include <linux/xattr.h> | ||
31 | |||
32 | enum ocfs2_xattr_type { | ||
33 | OCFS2_XATTR_INDEX_USER = 1, | ||
34 | OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS, | ||
35 | OCFS2_XATTR_INDEX_POSIX_ACL_DEFAULT, | ||
36 | OCFS2_XATTR_INDEX_TRUSTED, | ||
37 | OCFS2_XATTR_INDEX_SECURITY, | ||
38 | OCFS2_XATTR_MAX | ||
39 | }; | ||
40 | |||
41 | extern struct xattr_handler ocfs2_xattr_user_handler; | ||
42 | extern struct xattr_handler ocfs2_xattr_trusted_handler; | ||
43 | |||
44 | extern ssize_t ocfs2_listxattr(struct dentry *, char *, size_t); | ||
45 | extern int ocfs2_xattr_get(struct inode *, int, const char *, void *, size_t); | ||
46 | extern int ocfs2_xattr_set(struct inode *, int, const char *, const void *, | ||
47 | size_t, int); | ||
48 | extern int ocfs2_xattr_remove(struct inode *inode, struct buffer_head *di_bh); | ||
49 | extern struct xattr_handler *ocfs2_xattr_handlers[]; | ||
50 | |||
51 | #endif /* OCFS2_XATTR_H */ | ||
diff --git a/fs/ocfs2/xattr_trusted.c b/fs/ocfs2/xattr_trusted.c new file mode 100644 index 000000000000..4c589c447aaf --- /dev/null +++ b/fs/ocfs2/xattr_trusted.c | |||
@@ -0,0 +1,82 @@ | |||
1 | /* -*- mode: c; c-basic-offset: 8; -*- | ||
2 | * vim: noexpandtab sw=8 ts=8 sts=0: | ||
3 | * | ||
4 | * xattr_trusted.c | ||
5 | * | ||
6 | * Copyright (C) 2008 Oracle. All rights reserved. | ||
7 | * | ||
8 | * CREDITS: | ||
9 | * Lots of code in this file is taken from ext3. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or | ||
12 | * modify it under the terms of the GNU General Public | ||
13 | * License as published by the Free Software Foundation; either | ||
14 | * version 2 of the License, or (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19 | * General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public | ||
22 | * License along with this program; if not, write to the | ||
23 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
24 | * Boston, MA 021110-1307, USA. | ||
25 | */ | ||
26 | |||
27 | #include <linux/init.h> | ||
28 | #include <linux/module.h> | ||
29 | #include <linux/string.h> | ||
30 | |||
31 | #define MLOG_MASK_PREFIX ML_INODE | ||
32 | #include <cluster/masklog.h> | ||
33 | |||
34 | #include "ocfs2.h" | ||
35 | #include "alloc.h" | ||
36 | #include "dlmglue.h" | ||
37 | #include "file.h" | ||
38 | #include "ocfs2_fs.h" | ||
39 | #include "xattr.h" | ||
40 | |||
41 | #define XATTR_TRUSTED_PREFIX "trusted." | ||
42 | |||
43 | static size_t ocfs2_xattr_trusted_list(struct inode *inode, char *list, | ||
44 | size_t list_size, const char *name, | ||
45 | size_t name_len) | ||
46 | { | ||
47 | const size_t prefix_len = sizeof(XATTR_TRUSTED_PREFIX) - 1; | ||
48 | const size_t total_len = prefix_len + name_len + 1; | ||
49 | |||
50 | if (list && total_len <= list_size) { | ||
51 | memcpy(list, XATTR_TRUSTED_PREFIX, prefix_len); | ||
52 | memcpy(list + prefix_len, name, name_len); | ||
53 | list[prefix_len + name_len] = '\0'; | ||
54 | } | ||
55 | return total_len; | ||
56 | } | ||
57 | |||
58 | static int ocfs2_xattr_trusted_get(struct inode *inode, const char *name, | ||
59 | void *buffer, size_t size) | ||
60 | { | ||
61 | if (strcmp(name, "") == 0) | ||
62 | return -EINVAL; | ||
63 | return ocfs2_xattr_get(inode, OCFS2_XATTR_INDEX_TRUSTED, name, | ||
64 | buffer, size); | ||
65 | } | ||
66 | |||
67 | static int ocfs2_xattr_trusted_set(struct inode *inode, const char *name, | ||
68 | const void *value, size_t size, int flags) | ||
69 | { | ||
70 | if (strcmp(name, "") == 0) | ||
71 | return -EINVAL; | ||
72 | |||
73 | return ocfs2_xattr_set(inode, OCFS2_XATTR_INDEX_TRUSTED, name, value, | ||
74 | size, flags); | ||
75 | } | ||
76 | |||
77 | struct xattr_handler ocfs2_xattr_trusted_handler = { | ||
78 | .prefix = XATTR_TRUSTED_PREFIX, | ||
79 | .list = ocfs2_xattr_trusted_list, | ||
80 | .get = ocfs2_xattr_trusted_get, | ||
81 | .set = ocfs2_xattr_trusted_set, | ||
82 | }; | ||
diff --git a/fs/ocfs2/xattr_user.c b/fs/ocfs2/xattr_user.c new file mode 100644 index 000000000000..93ba71637788 --- /dev/null +++ b/fs/ocfs2/xattr_user.c | |||
@@ -0,0 +1,94 @@ | |||
1 | /* -*- mode: c; c-basic-offset: 8; -*- | ||
2 | * vim: noexpandtab sw=8 ts=8 sts=0: | ||
3 | * | ||
4 | * xattr_user.c | ||
5 | * | ||
6 | * Copyright (C) 2008 Oracle. All rights reserved. | ||
7 | * | ||
8 | * CREDITS: | ||
9 | * Lots of code in this file is taken from ext3. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or | ||
12 | * modify it under the terms of the GNU General Public | ||
13 | * License as published by the Free Software Foundation; either | ||
14 | * version 2 of the License, or (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19 | * General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public | ||
22 | * License along with this program; if not, write to the | ||
23 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
24 | * Boston, MA 021110-1307, USA. | ||
25 | */ | ||
26 | |||
27 | #include <linux/init.h> | ||
28 | #include <linux/module.h> | ||
29 | #include <linux/string.h> | ||
30 | |||
31 | #define MLOG_MASK_PREFIX ML_INODE | ||
32 | #include <cluster/masklog.h> | ||
33 | |||
34 | #include "ocfs2.h" | ||
35 | #include "alloc.h" | ||
36 | #include "dlmglue.h" | ||
37 | #include "file.h" | ||
38 | #include "ocfs2_fs.h" | ||
39 | #include "xattr.h" | ||
40 | |||
41 | #define XATTR_USER_PREFIX "user." | ||
42 | |||
43 | static size_t ocfs2_xattr_user_list(struct inode *inode, char *list, | ||
44 | size_t list_size, const char *name, | ||
45 | size_t name_len) | ||
46 | { | ||
47 | const size_t prefix_len = sizeof(XATTR_USER_PREFIX) - 1; | ||
48 | const size_t total_len = prefix_len + name_len + 1; | ||
49 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | ||
50 | |||
51 | if (osb->s_mount_opt & OCFS2_MOUNT_NOUSERXATTR) | ||
52 | return 0; | ||
53 | |||
54 | if (list && total_len <= list_size) { | ||
55 | memcpy(list, XATTR_USER_PREFIX, prefix_len); | ||
56 | memcpy(list + prefix_len, name, name_len); | ||
57 | list[prefix_len + name_len] = '\0'; | ||
58 | } | ||
59 | return total_len; | ||
60 | } | ||
61 | |||
62 | static int ocfs2_xattr_user_get(struct inode *inode, const char *name, | ||
63 | void *buffer, size_t size) | ||
64 | { | ||
65 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | ||
66 | |||
67 | if (strcmp(name, "") == 0) | ||
68 | return -EINVAL; | ||
69 | if (osb->s_mount_opt & OCFS2_MOUNT_NOUSERXATTR) | ||
70 | return -EOPNOTSUPP; | ||
71 | return ocfs2_xattr_get(inode, OCFS2_XATTR_INDEX_USER, name, | ||
72 | buffer, size); | ||
73 | } | ||
74 | |||
75 | static int ocfs2_xattr_user_set(struct inode *inode, const char *name, | ||
76 | const void *value, size_t size, int flags) | ||
77 | { | ||
78 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | ||
79 | |||
80 | if (strcmp(name, "") == 0) | ||
81 | return -EINVAL; | ||
82 | if (osb->s_mount_opt & OCFS2_MOUNT_NOUSERXATTR) | ||
83 | return -EOPNOTSUPP; | ||
84 | |||
85 | return ocfs2_xattr_set(inode, OCFS2_XATTR_INDEX_USER, name, value, | ||
86 | size, flags); | ||
87 | } | ||
88 | |||
89 | struct xattr_handler ocfs2_xattr_user_handler = { | ||
90 | .prefix = XATTR_USER_PREFIX, | ||
91 | .list = ocfs2_xattr_user_list, | ||
92 | .get = ocfs2_xattr_user_get, | ||
93 | .set = ocfs2_xattr_user_set, | ||
94 | }; | ||