0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include "hfsplus_fs.h"
0011 #include "hfsplus_raw.h"
0012
0013 static struct kmem_cache *hfsplus_attr_tree_cachep;
0014
0015 int __init hfsplus_create_attr_tree_cache(void)
0016 {
0017 if (hfsplus_attr_tree_cachep)
0018 return -EEXIST;
0019
0020 hfsplus_attr_tree_cachep =
0021 kmem_cache_create("hfsplus_attr_cache",
0022 sizeof(hfsplus_attr_entry), 0,
0023 SLAB_HWCACHE_ALIGN, NULL);
0024 if (!hfsplus_attr_tree_cachep)
0025 return -ENOMEM;
0026
0027 return 0;
0028 }
0029
0030 void hfsplus_destroy_attr_tree_cache(void)
0031 {
0032 kmem_cache_destroy(hfsplus_attr_tree_cachep);
0033 }
0034
0035 int hfsplus_attr_bin_cmp_key(const hfsplus_btree_key *k1,
0036 const hfsplus_btree_key *k2)
0037 {
0038 __be32 k1_cnid, k2_cnid;
0039
0040 k1_cnid = k1->attr.cnid;
0041 k2_cnid = k2->attr.cnid;
0042 if (k1_cnid != k2_cnid)
0043 return be32_to_cpu(k1_cnid) < be32_to_cpu(k2_cnid) ? -1 : 1;
0044
0045 return hfsplus_strcmp(
0046 (const struct hfsplus_unistr *)&k1->attr.key_name,
0047 (const struct hfsplus_unistr *)&k2->attr.key_name);
0048 }
0049
0050 int hfsplus_attr_build_key(struct super_block *sb, hfsplus_btree_key *key,
0051 u32 cnid, const char *name)
0052 {
0053 int len;
0054
0055 memset(key, 0, sizeof(struct hfsplus_attr_key));
0056 key->attr.cnid = cpu_to_be32(cnid);
0057 if (name) {
0058 int res = hfsplus_asc2uni(sb,
0059 (struct hfsplus_unistr *)&key->attr.key_name,
0060 HFSPLUS_ATTR_MAX_STRLEN, name, strlen(name));
0061 if (res)
0062 return res;
0063 len = be16_to_cpu(key->attr.key_name.length);
0064 } else {
0065 key->attr.key_name.length = 0;
0066 len = 0;
0067 }
0068
0069
0070
0071
0072
0073
0074
0075
0076 key->key_len =
0077 cpu_to_be16(offsetof(struct hfsplus_attr_key, key_name) +
0078 2 * len);
0079
0080 return 0;
0081 }
0082
0083 hfsplus_attr_entry *hfsplus_alloc_attr_entry(void)
0084 {
0085 return kmem_cache_alloc(hfsplus_attr_tree_cachep, GFP_KERNEL);
0086 }
0087
0088 void hfsplus_destroy_attr_entry(hfsplus_attr_entry *entry)
0089 {
0090 if (entry)
0091 kmem_cache_free(hfsplus_attr_tree_cachep, entry);
0092 }
0093
0094 #define HFSPLUS_INVALID_ATTR_RECORD -1
0095
0096 static int hfsplus_attr_build_record(hfsplus_attr_entry *entry, int record_type,
0097 u32 cnid, const void *value, size_t size)
0098 {
0099 if (record_type == HFSPLUS_ATTR_FORK_DATA) {
0100
0101
0102
0103
0104 memset(entry, 0, sizeof(*entry));
0105 return sizeof(struct hfsplus_attr_fork_data);
0106 } else if (record_type == HFSPLUS_ATTR_EXTENTS) {
0107
0108
0109
0110
0111 memset(entry, 0, sizeof(*entry));
0112 return sizeof(struct hfsplus_attr_extents);
0113 } else if (record_type == HFSPLUS_ATTR_INLINE_DATA) {
0114 u16 len;
0115
0116 memset(entry, 0, sizeof(struct hfsplus_attr_inline_data));
0117 entry->inline_data.record_type = cpu_to_be32(record_type);
0118 if (size <= HFSPLUS_MAX_INLINE_DATA_SIZE)
0119 len = size;
0120 else
0121 return HFSPLUS_INVALID_ATTR_RECORD;
0122 entry->inline_data.length = cpu_to_be16(len);
0123 memcpy(entry->inline_data.raw_bytes, value, len);
0124
0125
0126
0127
0128 len = round_up(len, 2);
0129 return offsetof(struct hfsplus_attr_inline_data, raw_bytes) +
0130 len;
0131 } else
0132 memset(entry, 0, sizeof(*entry));
0133
0134 return HFSPLUS_INVALID_ATTR_RECORD;
0135 }
0136
0137 int hfsplus_find_attr(struct super_block *sb, u32 cnid,
0138 const char *name, struct hfs_find_data *fd)
0139 {
0140 int err = 0;
0141
0142 hfs_dbg(ATTR_MOD, "find_attr: %s,%d\n", name ? name : NULL, cnid);
0143
0144 if (!HFSPLUS_SB(sb)->attr_tree) {
0145 pr_err("attributes file doesn't exist\n");
0146 return -EINVAL;
0147 }
0148
0149 if (name) {
0150 err = hfsplus_attr_build_key(sb, fd->search_key, cnid, name);
0151 if (err)
0152 goto failed_find_attr;
0153 err = hfs_brec_find(fd, hfs_find_rec_by_key);
0154 if (err)
0155 goto failed_find_attr;
0156 } else {
0157 err = hfsplus_attr_build_key(sb, fd->search_key, cnid, NULL);
0158 if (err)
0159 goto failed_find_attr;
0160 err = hfs_brec_find(fd, hfs_find_1st_rec_by_cnid);
0161 if (err)
0162 goto failed_find_attr;
0163 }
0164
0165 failed_find_attr:
0166 return err;
0167 }
0168
0169 int hfsplus_attr_exists(struct inode *inode, const char *name)
0170 {
0171 int err = 0;
0172 struct super_block *sb = inode->i_sb;
0173 struct hfs_find_data fd;
0174
0175 if (!HFSPLUS_SB(sb)->attr_tree)
0176 return 0;
0177
0178 err = hfs_find_init(HFSPLUS_SB(sb)->attr_tree, &fd);
0179 if (err)
0180 return 0;
0181
0182 err = hfsplus_find_attr(sb, inode->i_ino, name, &fd);
0183 if (err)
0184 goto attr_not_found;
0185
0186 hfs_find_exit(&fd);
0187 return 1;
0188
0189 attr_not_found:
0190 hfs_find_exit(&fd);
0191 return 0;
0192 }
0193
0194 int hfsplus_create_attr(struct inode *inode,
0195 const char *name,
0196 const void *value, size_t size)
0197 {
0198 struct super_block *sb = inode->i_sb;
0199 struct hfs_find_data fd;
0200 hfsplus_attr_entry *entry_ptr;
0201 int entry_size;
0202 int err;
0203
0204 hfs_dbg(ATTR_MOD, "create_attr: %s,%ld\n",
0205 name ? name : NULL, inode->i_ino);
0206
0207 if (!HFSPLUS_SB(sb)->attr_tree) {
0208 pr_err("attributes file doesn't exist\n");
0209 return -EINVAL;
0210 }
0211
0212 entry_ptr = hfsplus_alloc_attr_entry();
0213 if (!entry_ptr)
0214 return -ENOMEM;
0215
0216 err = hfs_find_init(HFSPLUS_SB(sb)->attr_tree, &fd);
0217 if (err)
0218 goto failed_init_create_attr;
0219
0220
0221 err = hfs_bmap_reserve(fd.tree, fd.tree->depth + 1);
0222 if (err)
0223 goto failed_create_attr;
0224
0225 if (name) {
0226 err = hfsplus_attr_build_key(sb, fd.search_key,
0227 inode->i_ino, name);
0228 if (err)
0229 goto failed_create_attr;
0230 } else {
0231 err = -EINVAL;
0232 goto failed_create_attr;
0233 }
0234
0235
0236 entry_size = hfsplus_attr_build_record(entry_ptr,
0237 HFSPLUS_ATTR_INLINE_DATA,
0238 inode->i_ino,
0239 value, size);
0240 if (entry_size == HFSPLUS_INVALID_ATTR_RECORD) {
0241 err = -EINVAL;
0242 goto failed_create_attr;
0243 }
0244
0245 err = hfs_brec_find(&fd, hfs_find_rec_by_key);
0246 if (err != -ENOENT) {
0247 if (!err)
0248 err = -EEXIST;
0249 goto failed_create_attr;
0250 }
0251
0252 err = hfs_brec_insert(&fd, entry_ptr, entry_size);
0253 if (err)
0254 goto failed_create_attr;
0255
0256 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ATTR_DIRTY);
0257
0258 failed_create_attr:
0259 hfs_find_exit(&fd);
0260
0261 failed_init_create_attr:
0262 hfsplus_destroy_attr_entry(entry_ptr);
0263 return err;
0264 }
0265
0266 static int __hfsplus_delete_attr(struct inode *inode, u32 cnid,
0267 struct hfs_find_data *fd)
0268 {
0269 int err = 0;
0270 __be32 found_cnid, record_type;
0271
0272 hfs_bnode_read(fd->bnode, &found_cnid,
0273 fd->keyoffset +
0274 offsetof(struct hfsplus_attr_key, cnid),
0275 sizeof(__be32));
0276 if (cnid != be32_to_cpu(found_cnid))
0277 return -ENOENT;
0278
0279 hfs_bnode_read(fd->bnode, &record_type,
0280 fd->entryoffset, sizeof(record_type));
0281
0282 switch (be32_to_cpu(record_type)) {
0283 case HFSPLUS_ATTR_INLINE_DATA:
0284
0285 break;
0286 case HFSPLUS_ATTR_FORK_DATA:
0287 case HFSPLUS_ATTR_EXTENTS:
0288 pr_err("only inline data xattr are supported\n");
0289 return -EOPNOTSUPP;
0290 default:
0291 pr_err("invalid extended attribute record\n");
0292 return -ENOENT;
0293 }
0294
0295
0296 hfs_bnode_read(fd->bnode, fd->search_key,
0297 fd->keyoffset, fd->keylength);
0298
0299 err = hfs_brec_remove(fd);
0300 if (err)
0301 return err;
0302
0303 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ATTR_DIRTY);
0304 return err;
0305 }
0306
0307 int hfsplus_delete_attr(struct inode *inode, const char *name)
0308 {
0309 int err = 0;
0310 struct super_block *sb = inode->i_sb;
0311 struct hfs_find_data fd;
0312
0313 hfs_dbg(ATTR_MOD, "delete_attr: %s,%ld\n",
0314 name ? name : NULL, inode->i_ino);
0315
0316 if (!HFSPLUS_SB(sb)->attr_tree) {
0317 pr_err("attributes file doesn't exist\n");
0318 return -EINVAL;
0319 }
0320
0321 err = hfs_find_init(HFSPLUS_SB(sb)->attr_tree, &fd);
0322 if (err)
0323 return err;
0324
0325
0326 err = hfs_bmap_reserve(fd.tree, fd.tree->depth);
0327 if (err)
0328 goto out;
0329
0330 if (name) {
0331 err = hfsplus_attr_build_key(sb, fd.search_key,
0332 inode->i_ino, name);
0333 if (err)
0334 goto out;
0335 } else {
0336 pr_err("invalid extended attribute name\n");
0337 err = -EINVAL;
0338 goto out;
0339 }
0340
0341 err = hfs_brec_find(&fd, hfs_find_rec_by_key);
0342 if (err)
0343 goto out;
0344
0345 err = __hfsplus_delete_attr(inode, inode->i_ino, &fd);
0346 if (err)
0347 goto out;
0348
0349 out:
0350 hfs_find_exit(&fd);
0351 return err;
0352 }
0353
0354 int hfsplus_delete_all_attrs(struct inode *dir, u32 cnid)
0355 {
0356 int err = 0;
0357 struct hfs_find_data fd;
0358
0359 hfs_dbg(ATTR_MOD, "delete_all_attrs: %d\n", cnid);
0360
0361 if (!HFSPLUS_SB(dir->i_sb)->attr_tree) {
0362 pr_err("attributes file doesn't exist\n");
0363 return -EINVAL;
0364 }
0365
0366 err = hfs_find_init(HFSPLUS_SB(dir->i_sb)->attr_tree, &fd);
0367 if (err)
0368 return err;
0369
0370 for (;;) {
0371 err = hfsplus_find_attr(dir->i_sb, cnid, NULL, &fd);
0372 if (err) {
0373 if (err != -ENOENT)
0374 pr_err("xattr search failed\n");
0375 goto end_delete_all;
0376 }
0377
0378 err = __hfsplus_delete_attr(dir, cnid, &fd);
0379 if (err)
0380 goto end_delete_all;
0381 }
0382
0383 end_delete_all:
0384 hfs_find_exit(&fd);
0385 return err;
0386 }