2 * linux/fs/hfsplus/attributes.c
4 * Vyacheslav Dubeyko <slava@dubeyko.com>
6 * Handling of records in attributes tree
9 #include "hfsplus_fs.h"
10 #include "hfsplus_raw.h"
12 static struct kmem_cache
*hfsplus_attr_tree_cachep
;
14 int __init
hfsplus_create_attr_tree_cache(void)
16 if (hfsplus_attr_tree_cachep
)
19 hfsplus_attr_tree_cachep
=
20 kmem_cache_create("hfsplus_attr_cache",
21 sizeof(hfsplus_attr_entry
), 0,
22 SLAB_HWCACHE_ALIGN
, NULL
);
23 if (!hfsplus_attr_tree_cachep
)
29 void hfsplus_destroy_attr_tree_cache(void)
31 kmem_cache_destroy(hfsplus_attr_tree_cachep
);
34 int hfsplus_attr_bin_cmp_key(const hfsplus_btree_key
*k1
,
35 const hfsplus_btree_key
*k2
)
37 __be32 k1_cnid
, k2_cnid
;
39 k1_cnid
= k1
->attr
.cnid
;
40 k2_cnid
= k2
->attr
.cnid
;
41 if (k1_cnid
!= k2_cnid
)
42 return be32_to_cpu(k1_cnid
) < be32_to_cpu(k2_cnid
) ? -1 : 1;
44 return hfsplus_strcmp(
45 (const struct hfsplus_unistr
*)&k1
->attr
.key_name
,
46 (const struct hfsplus_unistr
*)&k2
->attr
.key_name
);
49 int hfsplus_attr_build_key(struct super_block
*sb
, hfsplus_btree_key
*key
,
50 u32 cnid
, const char *name
)
54 memset(key
, 0, sizeof(struct hfsplus_attr_key
));
55 key
->attr
.cnid
= cpu_to_be32(cnid
);
57 int res
= hfsplus_asc2uni(sb
,
58 (struct hfsplus_unistr
*)&key
->attr
.key_name
,
59 HFSPLUS_ATTR_MAX_STRLEN
, name
, strlen(name
));
62 len
= be16_to_cpu(key
->attr
.key_name
.length
);
64 key
->attr
.key_name
.length
= 0;
68 /* The length of the key, as stored in key_len field, does not include
69 * the size of the key_len field itself.
70 * So, offsetof(hfsplus_attr_key, key_name) is a trick because
71 * it takes into consideration key_len field (__be16) of
72 * hfsplus_attr_key structure instead of length field (__be16) of
73 * hfsplus_attr_unistr structure.
76 cpu_to_be16(offsetof(struct hfsplus_attr_key
, key_name
) +
82 hfsplus_attr_entry
*hfsplus_alloc_attr_entry(void)
84 return kmem_cache_alloc(hfsplus_attr_tree_cachep
, GFP_KERNEL
);
87 void hfsplus_destroy_attr_entry(hfsplus_attr_entry
*entry
)
90 kmem_cache_free(hfsplus_attr_tree_cachep
, entry
);
93 #define HFSPLUS_INVALID_ATTR_RECORD -1
95 static int hfsplus_attr_build_record(hfsplus_attr_entry
*entry
, int record_type
,
96 u32 cnid
, const void *value
, size_t size
)
98 if (record_type
== HFSPLUS_ATTR_FORK_DATA
) {
100 * Mac OS X supports only inline data attributes.
103 memset(entry
, 0, sizeof(*entry
));
104 return sizeof(struct hfsplus_attr_fork_data
);
105 } else if (record_type
== HFSPLUS_ATTR_EXTENTS
) {
107 * Mac OS X supports only inline data attributes.
110 memset(entry
, 0, sizeof(*entry
));
111 return sizeof(struct hfsplus_attr_extents
);
112 } else if (record_type
== HFSPLUS_ATTR_INLINE_DATA
) {
115 memset(entry
, 0, sizeof(struct hfsplus_attr_inline_data
));
116 entry
->inline_data
.record_type
= cpu_to_be32(record_type
);
117 if (size
<= HFSPLUS_MAX_INLINE_DATA_SIZE
)
120 return HFSPLUS_INVALID_ATTR_RECORD
;
121 entry
->inline_data
.length
= cpu_to_be16(len
);
122 memcpy(entry
->inline_data
.raw_bytes
, value
, len
);
124 * Align len on two-byte boundary.
125 * It needs to add pad byte if we have odd len.
127 len
= round_up(len
, 2);
128 return offsetof(struct hfsplus_attr_inline_data
, raw_bytes
) +
130 } else /* invalid input */
131 memset(entry
, 0, sizeof(*entry
));
133 return HFSPLUS_INVALID_ATTR_RECORD
;
136 int hfsplus_find_attr(struct super_block
*sb
, u32 cnid
,
137 const char *name
, struct hfs_find_data
*fd
)
141 hfs_dbg(ATTR_MOD
, "find_attr: %s,%d\n", name
? name
: NULL
, cnid
);
143 if (!HFSPLUS_SB(sb
)->attr_tree
) {
144 pr_err("attributes file doesn't exist\n");
149 err
= hfsplus_attr_build_key(sb
, fd
->search_key
, cnid
, name
);
151 goto failed_find_attr
;
152 err
= hfs_brec_find(fd
, hfs_find_rec_by_key
);
154 goto failed_find_attr
;
156 err
= hfsplus_attr_build_key(sb
, fd
->search_key
, cnid
, NULL
);
158 goto failed_find_attr
;
159 err
= hfs_brec_find(fd
, hfs_find_1st_rec_by_cnid
);
161 goto failed_find_attr
;
168 int hfsplus_attr_exists(struct inode
*inode
, const char *name
)
171 struct super_block
*sb
= inode
->i_sb
;
172 struct hfs_find_data fd
;
174 if (!HFSPLUS_SB(sb
)->attr_tree
)
177 err
= hfs_find_init(HFSPLUS_SB(sb
)->attr_tree
, &fd
);
181 err
= hfsplus_find_attr(sb
, inode
->i_ino
, name
, &fd
);
193 int hfsplus_create_attr(struct inode
*inode
,
195 const void *value
, size_t size
)
197 struct super_block
*sb
= inode
->i_sb
;
198 struct hfs_find_data fd
;
199 hfsplus_attr_entry
*entry_ptr
;
203 hfs_dbg(ATTR_MOD
, "create_attr: %s,%ld\n",
204 name
? name
: NULL
, inode
->i_ino
);
206 if (!HFSPLUS_SB(sb
)->attr_tree
) {
207 pr_err("attributes file doesn't exist\n");
211 entry_ptr
= hfsplus_alloc_attr_entry();
215 err
= hfs_find_init(HFSPLUS_SB(sb
)->attr_tree
, &fd
);
217 goto failed_init_create_attr
;
219 /* Fail early and avoid ENOSPC during the btree operation */
220 err
= hfs_bmap_reserve(fd
.tree
, fd
.tree
->depth
+ 1);
222 goto failed_create_attr
;
225 err
= hfsplus_attr_build_key(sb
, fd
.search_key
,
228 goto failed_create_attr
;
231 goto failed_create_attr
;
234 /* Mac OS X supports only inline data attributes. */
235 entry_size
= hfsplus_attr_build_record(entry_ptr
,
236 HFSPLUS_ATTR_INLINE_DATA
,
239 if (entry_size
== HFSPLUS_INVALID_ATTR_RECORD
) {
241 goto failed_create_attr
;
244 err
= hfs_brec_find(&fd
, hfs_find_rec_by_key
);
245 if (err
!= -ENOENT
) {
248 goto failed_create_attr
;
251 err
= hfs_brec_insert(&fd
, entry_ptr
, entry_size
);
253 goto failed_create_attr
;
255 hfsplus_mark_inode_dirty(inode
, HFSPLUS_I_ATTR_DIRTY
);
260 failed_init_create_attr
:
261 hfsplus_destroy_attr_entry(entry_ptr
);
265 static int __hfsplus_delete_attr(struct inode
*inode
, u32 cnid
,
266 struct hfs_find_data
*fd
)
269 __be32 found_cnid
, record_type
;
271 hfs_bnode_read(fd
->bnode
, &found_cnid
,
273 offsetof(struct hfsplus_attr_key
, cnid
),
275 if (cnid
!= be32_to_cpu(found_cnid
))
278 hfs_bnode_read(fd
->bnode
, &record_type
,
279 fd
->entryoffset
, sizeof(record_type
));
281 switch (be32_to_cpu(record_type
)) {
282 case HFSPLUS_ATTR_INLINE_DATA
:
283 /* All is OK. Do nothing. */
285 case HFSPLUS_ATTR_FORK_DATA
:
286 case HFSPLUS_ATTR_EXTENTS
:
287 pr_err("only inline data xattr are supported\n");
290 pr_err("invalid extended attribute record\n");
294 /* Avoid btree corruption */
295 hfs_bnode_read(fd
->bnode
, fd
->search_key
,
296 fd
->keyoffset
, fd
->keylength
);
298 err
= hfs_brec_remove(fd
);
302 hfsplus_mark_inode_dirty(inode
, HFSPLUS_I_ATTR_DIRTY
);
306 int hfsplus_delete_attr(struct inode
*inode
, const char *name
)
309 struct super_block
*sb
= inode
->i_sb
;
310 struct hfs_find_data fd
;
312 hfs_dbg(ATTR_MOD
, "delete_attr: %s,%ld\n",
313 name
? name
: NULL
, inode
->i_ino
);
315 if (!HFSPLUS_SB(sb
)->attr_tree
) {
316 pr_err("attributes file doesn't exist\n");
320 err
= hfs_find_init(HFSPLUS_SB(sb
)->attr_tree
, &fd
);
324 /* Fail early and avoid ENOSPC during the btree operation */
325 err
= hfs_bmap_reserve(fd
.tree
, fd
.tree
->depth
);
330 err
= hfsplus_attr_build_key(sb
, fd
.search_key
,
335 pr_err("invalid extended attribute name\n");
340 err
= hfs_brec_find(&fd
, hfs_find_rec_by_key
);
344 err
= __hfsplus_delete_attr(inode
, inode
->i_ino
, &fd
);
353 int hfsplus_delete_all_attrs(struct inode
*dir
, u32 cnid
)
356 struct hfs_find_data fd
;
358 hfs_dbg(ATTR_MOD
, "delete_all_attrs: %d\n", cnid
);
360 if (!HFSPLUS_SB(dir
->i_sb
)->attr_tree
) {
361 pr_err("attributes file doesn't exist\n");
365 err
= hfs_find_init(HFSPLUS_SB(dir
->i_sb
)->attr_tree
, &fd
);
370 err
= hfsplus_find_attr(dir
->i_sb
, cnid
, NULL
, &fd
);
373 pr_err("xattr search failed\n");
377 err
= __hfsplus_delete_attr(dir
, cnid
, &fd
);