1 // SPDX-License-Identifier: GPL-2.0
3 * linux/fs/hfsplus/attributes.c
5 * Vyacheslav Dubeyko <slava@dubeyko.com>
7 * Handling of records in attributes tree
10 #include "hfsplus_fs.h"
11 #include "hfsplus_raw.h"
13 static struct kmem_cache
*hfsplus_attr_tree_cachep
;
15 int __init
hfsplus_create_attr_tree_cache(void)
17 if (hfsplus_attr_tree_cachep
)
20 hfsplus_attr_tree_cachep
=
21 kmem_cache_create("hfsplus_attr_cache",
22 sizeof(hfsplus_attr_entry
), 0,
23 SLAB_HWCACHE_ALIGN
, NULL
);
24 if (!hfsplus_attr_tree_cachep
)
30 void hfsplus_destroy_attr_tree_cache(void)
32 kmem_cache_destroy(hfsplus_attr_tree_cachep
);
35 int hfsplus_attr_bin_cmp_key(const hfsplus_btree_key
*k1
,
36 const hfsplus_btree_key
*k2
)
38 __be32 k1_cnid
, k2_cnid
;
40 k1_cnid
= k1
->attr
.cnid
;
41 k2_cnid
= k2
->attr
.cnid
;
42 if (k1_cnid
!= k2_cnid
)
43 return be32_to_cpu(k1_cnid
) < be32_to_cpu(k2_cnid
) ? -1 : 1;
45 return hfsplus_strcmp(
46 (const struct hfsplus_unistr
*)&k1
->attr
.key_name
,
47 (const struct hfsplus_unistr
*)&k2
->attr
.key_name
);
50 int hfsplus_attr_build_key(struct super_block
*sb
, hfsplus_btree_key
*key
,
51 u32 cnid
, const char *name
)
55 memset(key
, 0, sizeof(struct hfsplus_attr_key
));
56 key
->attr
.cnid
= cpu_to_be32(cnid
);
58 int res
= hfsplus_asc2uni(sb
,
59 (struct hfsplus_unistr
*)&key
->attr
.key_name
,
60 HFSPLUS_ATTR_MAX_STRLEN
, name
, strlen(name
));
63 len
= be16_to_cpu(key
->attr
.key_name
.length
);
65 key
->attr
.key_name
.length
= 0;
69 /* The length of the key, as stored in key_len field, does not include
70 * the size of the key_len field itself.
71 * So, offsetof(hfsplus_attr_key, key_name) is a trick because
72 * it takes into consideration key_len field (__be16) of
73 * hfsplus_attr_key structure instead of length field (__be16) of
74 * hfsplus_attr_unistr structure.
77 cpu_to_be16(offsetof(struct hfsplus_attr_key
, key_name
) +
83 hfsplus_attr_entry
*hfsplus_alloc_attr_entry(void)
85 return kmem_cache_alloc(hfsplus_attr_tree_cachep
, GFP_KERNEL
);
88 void hfsplus_destroy_attr_entry(hfsplus_attr_entry
*entry
)
91 kmem_cache_free(hfsplus_attr_tree_cachep
, entry
);
94 #define HFSPLUS_INVALID_ATTR_RECORD -1
96 static int hfsplus_attr_build_record(hfsplus_attr_entry
*entry
, int record_type
,
97 u32 cnid
, const void *value
, size_t size
)
99 if (record_type
== HFSPLUS_ATTR_FORK_DATA
) {
101 * Mac OS X supports only inline data attributes.
104 memset(entry
, 0, sizeof(*entry
));
105 return sizeof(struct hfsplus_attr_fork_data
);
106 } else if (record_type
== HFSPLUS_ATTR_EXTENTS
) {
108 * Mac OS X supports only inline data attributes.
111 memset(entry
, 0, sizeof(*entry
));
112 return sizeof(struct hfsplus_attr_extents
);
113 } else if (record_type
== HFSPLUS_ATTR_INLINE_DATA
) {
116 memset(entry
, 0, sizeof(struct hfsplus_attr_inline_data
));
117 entry
->inline_data
.record_type
= cpu_to_be32(record_type
);
118 if (size
<= HFSPLUS_MAX_INLINE_DATA_SIZE
)
121 return HFSPLUS_INVALID_ATTR_RECORD
;
122 entry
->inline_data
.length
= cpu_to_be16(len
);
123 memcpy(entry
->inline_data
.raw_bytes
, value
, len
);
125 * Align len on two-byte boundary.
126 * It needs to add pad byte if we have odd len.
128 len
= round_up(len
, 2);
129 return offsetof(struct hfsplus_attr_inline_data
, raw_bytes
) +
131 } else /* invalid input */
132 memset(entry
, 0, sizeof(*entry
));
134 return HFSPLUS_INVALID_ATTR_RECORD
;
137 int hfsplus_find_attr(struct super_block
*sb
, u32 cnid
,
138 const char *name
, struct hfs_find_data
*fd
)
142 hfs_dbg(ATTR_MOD
, "find_attr: %s,%d\n", name
? name
: NULL
, cnid
);
144 if (!HFSPLUS_SB(sb
)->attr_tree
) {
145 pr_err("attributes file doesn't exist\n");
150 err
= hfsplus_attr_build_key(sb
, fd
->search_key
, cnid
, name
);
152 goto failed_find_attr
;
153 err
= hfs_brec_find(fd
, hfs_find_rec_by_key
);
155 goto failed_find_attr
;
157 err
= hfsplus_attr_build_key(sb
, fd
->search_key
, cnid
, NULL
);
159 goto failed_find_attr
;
160 err
= hfs_brec_find(fd
, hfs_find_1st_rec_by_cnid
);
162 goto failed_find_attr
;
169 int hfsplus_attr_exists(struct inode
*inode
, const char *name
)
172 struct super_block
*sb
= inode
->i_sb
;
173 struct hfs_find_data fd
;
175 if (!HFSPLUS_SB(sb
)->attr_tree
)
178 err
= hfs_find_init(HFSPLUS_SB(sb
)->attr_tree
, &fd
);
182 err
= hfsplus_find_attr(sb
, inode
->i_ino
, name
, &fd
);
194 int hfsplus_create_attr(struct inode
*inode
,
196 const void *value
, size_t size
)
198 struct super_block
*sb
= inode
->i_sb
;
199 struct hfs_find_data fd
;
200 hfsplus_attr_entry
*entry_ptr
;
204 hfs_dbg(ATTR_MOD
, "create_attr: %s,%ld\n",
205 name
? name
: NULL
, inode
->i_ino
);
207 if (!HFSPLUS_SB(sb
)->attr_tree
) {
208 pr_err("attributes file doesn't exist\n");
212 entry_ptr
= hfsplus_alloc_attr_entry();
216 err
= hfs_find_init(HFSPLUS_SB(sb
)->attr_tree
, &fd
);
218 goto failed_init_create_attr
;
220 /* Fail early and avoid ENOSPC during the btree operation */
221 err
= hfs_bmap_reserve(fd
.tree
, fd
.tree
->depth
+ 1);
223 goto failed_create_attr
;
226 err
= hfsplus_attr_build_key(sb
, fd
.search_key
,
229 goto failed_create_attr
;
232 goto failed_create_attr
;
235 /* Mac OS X supports only inline data attributes. */
236 entry_size
= hfsplus_attr_build_record(entry_ptr
,
237 HFSPLUS_ATTR_INLINE_DATA
,
240 if (entry_size
== HFSPLUS_INVALID_ATTR_RECORD
) {
242 goto failed_create_attr
;
245 err
= hfs_brec_find(&fd
, hfs_find_rec_by_key
);
246 if (err
!= -ENOENT
) {
249 goto failed_create_attr
;
252 err
= hfs_brec_insert(&fd
, entry_ptr
, entry_size
);
254 goto failed_create_attr
;
256 hfsplus_mark_inode_dirty(inode
, HFSPLUS_I_ATTR_DIRTY
);
261 failed_init_create_attr
:
262 hfsplus_destroy_attr_entry(entry_ptr
);
266 static int __hfsplus_delete_attr(struct inode
*inode
, u32 cnid
,
267 struct hfs_find_data
*fd
)
270 __be32 found_cnid
, record_type
;
272 hfs_bnode_read(fd
->bnode
, &found_cnid
,
274 offsetof(struct hfsplus_attr_key
, cnid
),
276 if (cnid
!= be32_to_cpu(found_cnid
))
279 hfs_bnode_read(fd
->bnode
, &record_type
,
280 fd
->entryoffset
, sizeof(record_type
));
282 switch (be32_to_cpu(record_type
)) {
283 case HFSPLUS_ATTR_INLINE_DATA
:
284 /* All is OK. Do nothing. */
286 case HFSPLUS_ATTR_FORK_DATA
:
287 case HFSPLUS_ATTR_EXTENTS
:
288 pr_err("only inline data xattr are supported\n");
291 pr_err("invalid extended attribute record\n");
295 /* Avoid btree corruption */
296 hfs_bnode_read(fd
->bnode
, fd
->search_key
,
297 fd
->keyoffset
, fd
->keylength
);
299 err
= hfs_brec_remove(fd
);
303 hfsplus_mark_inode_dirty(inode
, HFSPLUS_I_ATTR_DIRTY
);
307 int hfsplus_delete_attr(struct inode
*inode
, const char *name
)
310 struct super_block
*sb
= inode
->i_sb
;
311 struct hfs_find_data fd
;
313 hfs_dbg(ATTR_MOD
, "delete_attr: %s,%ld\n",
314 name
? name
: NULL
, inode
->i_ino
);
316 if (!HFSPLUS_SB(sb
)->attr_tree
) {
317 pr_err("attributes file doesn't exist\n");
321 err
= hfs_find_init(HFSPLUS_SB(sb
)->attr_tree
, &fd
);
325 /* Fail early and avoid ENOSPC during the btree operation */
326 err
= hfs_bmap_reserve(fd
.tree
, fd
.tree
->depth
);
331 err
= hfsplus_attr_build_key(sb
, fd
.search_key
,
336 pr_err("invalid extended attribute name\n");
341 err
= hfs_brec_find(&fd
, hfs_find_rec_by_key
);
345 err
= __hfsplus_delete_attr(inode
, inode
->i_ino
, &fd
);
354 int hfsplus_delete_all_attrs(struct inode
*dir
, u32 cnid
)
357 struct hfs_find_data fd
;
359 hfs_dbg(ATTR_MOD
, "delete_all_attrs: %d\n", cnid
);
361 if (!HFSPLUS_SB(dir
->i_sb
)->attr_tree
) {
362 pr_err("attributes file doesn't exist\n");
366 err
= hfs_find_init(HFSPLUS_SB(dir
->i_sb
)->attr_tree
, &fd
);
371 err
= hfsplus_find_attr(dir
->i_sb
, cnid
, NULL
, &fd
);
374 pr_err("xattr search failed\n");
378 err
= __hfsplus_delete_attr(dir
, cnid
, &fd
);