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 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
);
58 if (len
> HFSPLUS_ATTR_MAX_STRLEN
) {
59 printk(KERN_ERR
"hfs: invalid xattr name's length\n");
63 (struct hfsplus_unistr
*)&key
->attr
.key_name
,
64 HFSPLUS_ATTR_MAX_STRLEN
, name
, len
);
65 len
= be16_to_cpu(key
->attr
.key_name
.length
);
67 key
->attr
.key_name
.length
= 0;
71 /* The length of the key, as stored in key_len field, does not include
72 * the size of the key_len field itself.
73 * So, offsetof(hfsplus_attr_key, key_name) is a trick because
74 * it takes into consideration key_len field (__be16) of
75 * hfsplus_attr_key structure instead of length field (__be16) of
76 * hfsplus_attr_unistr structure.
79 cpu_to_be16(offsetof(struct hfsplus_attr_key
, key_name
) +
85 void hfsplus_attr_build_key_uni(hfsplus_btree_key
*key
,
87 struct hfsplus_attr_unistr
*name
)
91 memset(key
, 0, sizeof(struct hfsplus_attr_key
));
92 ustrlen
= be16_to_cpu(name
->length
);
93 key
->attr
.cnid
= cpu_to_be32(cnid
);
94 key
->attr
.key_name
.length
= cpu_to_be16(ustrlen
);
96 memcpy(key
->attr
.key_name
.unicode
, name
->unicode
, ustrlen
);
98 /* The length of the key, as stored in key_len field, does not include
99 * the size of the key_len field itself.
100 * So, offsetof(hfsplus_attr_key, key_name) is a trick because
101 * it takes into consideration key_len field (__be16) of
102 * hfsplus_attr_key structure instead of length field (__be16) of
103 * hfsplus_attr_unistr structure.
106 cpu_to_be16(offsetof(struct hfsplus_attr_key
, key_name
) +
110 hfsplus_attr_entry
*hfsplus_alloc_attr_entry(void)
112 return kmem_cache_alloc(hfsplus_attr_tree_cachep
, GFP_KERNEL
);
115 void hfsplus_destroy_attr_entry(hfsplus_attr_entry
*entry
)
118 kmem_cache_free(hfsplus_attr_tree_cachep
, entry
);
121 #define HFSPLUS_INVALID_ATTR_RECORD -1
123 static int hfsplus_attr_build_record(hfsplus_attr_entry
*entry
, int record_type
,
124 u32 cnid
, const void *value
, size_t size
)
126 if (record_type
== HFSPLUS_ATTR_FORK_DATA
) {
128 * Mac OS X supports only inline data attributes.
131 memset(entry
, 0, sizeof(*entry
));
132 return sizeof(struct hfsplus_attr_fork_data
);
133 } else if (record_type
== HFSPLUS_ATTR_EXTENTS
) {
135 * Mac OS X supports only inline data attributes.
138 memset(entry
, 0, sizeof(*entry
));
139 return sizeof(struct hfsplus_attr_extents
);
140 } else if (record_type
== HFSPLUS_ATTR_INLINE_DATA
) {
143 memset(entry
, 0, sizeof(struct hfsplus_attr_inline_data
));
144 entry
->inline_data
.record_type
= cpu_to_be32(record_type
);
145 if (size
<= HFSPLUS_MAX_INLINE_DATA_SIZE
)
148 return HFSPLUS_INVALID_ATTR_RECORD
;
149 entry
->inline_data
.length
= cpu_to_be16(len
);
150 memcpy(entry
->inline_data
.raw_bytes
, value
, len
);
152 * Align len on two-byte boundary.
153 * It needs to add pad byte if we have odd len.
155 len
= round_up(len
, 2);
156 return offsetof(struct hfsplus_attr_inline_data
, raw_bytes
) +
158 } else /* invalid input */
159 memset(entry
, 0, sizeof(*entry
));
161 return HFSPLUS_INVALID_ATTR_RECORD
;
164 int hfsplus_find_attr(struct super_block
*sb
, u32 cnid
,
165 const char *name
, struct hfs_find_data
*fd
)
169 dprint(DBG_ATTR_MOD
, "find_attr: %s,%d\n", name
? name
: NULL
, cnid
);
171 if (!HFSPLUS_SB(sb
)->attr_tree
) {
172 printk(KERN_ERR
"hfs: attributes file doesn't exist\n");
177 err
= hfsplus_attr_build_key(sb
, fd
->search_key
, cnid
, name
);
179 goto failed_find_attr
;
180 err
= hfs_brec_find(fd
, hfs_find_rec_by_key
);
182 goto failed_find_attr
;
184 err
= hfsplus_attr_build_key(sb
, fd
->search_key
, cnid
, NULL
);
186 goto failed_find_attr
;
187 err
= hfs_brec_find(fd
, hfs_find_1st_rec_by_cnid
);
189 goto failed_find_attr
;
196 int hfsplus_attr_exists(struct inode
*inode
, const char *name
)
199 struct super_block
*sb
= inode
->i_sb
;
200 struct hfs_find_data fd
;
202 if (!HFSPLUS_SB(sb
)->attr_tree
)
205 err
= hfs_find_init(HFSPLUS_SB(sb
)->attr_tree
, &fd
);
209 err
= hfsplus_find_attr(sb
, inode
->i_ino
, name
, &fd
);
221 int hfsplus_create_attr(struct inode
*inode
,
223 const void *value
, size_t size
)
225 struct super_block
*sb
= inode
->i_sb
;
226 struct hfs_find_data fd
;
227 hfsplus_attr_entry
*entry_ptr
;
231 dprint(DBG_ATTR_MOD
, "create_attr: %s,%ld\n",
232 name
? name
: NULL
, inode
->i_ino
);
234 if (!HFSPLUS_SB(sb
)->attr_tree
) {
235 printk(KERN_ERR
"hfs: attributes file doesn't exist\n");
239 entry_ptr
= hfsplus_alloc_attr_entry();
243 err
= hfs_find_init(HFSPLUS_SB(sb
)->attr_tree
, &fd
);
245 goto failed_init_create_attr
;
248 err
= hfsplus_attr_build_key(sb
, fd
.search_key
,
251 goto failed_create_attr
;
254 goto failed_create_attr
;
257 /* Mac OS X supports only inline data attributes. */
258 entry_size
= hfsplus_attr_build_record(entry_ptr
,
259 HFSPLUS_ATTR_INLINE_DATA
,
262 if (entry_size
== HFSPLUS_INVALID_ATTR_RECORD
) {
264 goto failed_create_attr
;
267 err
= hfs_brec_find(&fd
, hfs_find_rec_by_key
);
268 if (err
!= -ENOENT
) {
271 goto failed_create_attr
;
274 err
= hfs_brec_insert(&fd
, entry_ptr
, entry_size
);
276 goto failed_create_attr
;
278 hfsplus_mark_inode_dirty(inode
, HFSPLUS_I_ATTR_DIRTY
);
283 failed_init_create_attr
:
284 hfsplus_destroy_attr_entry(entry_ptr
);
288 static int __hfsplus_delete_attr(struct inode
*inode
, u32 cnid
,
289 struct hfs_find_data
*fd
)
292 __be32 found_cnid
, record_type
;
294 hfs_bnode_read(fd
->bnode
, &found_cnid
,
296 offsetof(struct hfsplus_attr_key
, cnid
),
298 if (cnid
!= be32_to_cpu(found_cnid
))
301 hfs_bnode_read(fd
->bnode
, &record_type
,
302 fd
->entryoffset
, sizeof(record_type
));
304 switch (be32_to_cpu(record_type
)) {
305 case HFSPLUS_ATTR_INLINE_DATA
:
306 /* All is OK. Do nothing. */
308 case HFSPLUS_ATTR_FORK_DATA
:
309 case HFSPLUS_ATTR_EXTENTS
:
310 printk(KERN_ERR
"hfs: only inline data xattr are supported\n");
313 printk(KERN_ERR
"hfs: invalid extended attribute record\n");
317 err
= hfs_brec_remove(fd
);
321 hfsplus_mark_inode_dirty(inode
, HFSPLUS_I_ATTR_DIRTY
);
325 int hfsplus_delete_attr(struct inode
*inode
, const char *name
)
328 struct super_block
*sb
= inode
->i_sb
;
329 struct hfs_find_data fd
;
331 dprint(DBG_ATTR_MOD
, "delete_attr: %s,%ld\n",
332 name
? name
: NULL
, inode
->i_ino
);
334 if (!HFSPLUS_SB(sb
)->attr_tree
) {
335 printk(KERN_ERR
"hfs: attributes file doesn't exist\n");
339 err
= hfs_find_init(HFSPLUS_SB(sb
)->attr_tree
, &fd
);
344 err
= hfsplus_attr_build_key(sb
, fd
.search_key
,
349 printk(KERN_ERR
"hfs: invalid extended attribute name\n");
354 err
= hfs_brec_find(&fd
, hfs_find_rec_by_key
);
358 err
= __hfsplus_delete_attr(inode
, inode
->i_ino
, &fd
);
367 int hfsplus_delete_all_attrs(struct inode
*dir
, u32 cnid
)
370 struct hfs_find_data fd
;
372 dprint(DBG_ATTR_MOD
, "delete_all_attrs: %d\n", cnid
);
374 if (!HFSPLUS_SB(dir
->i_sb
)->attr_tree
) {
375 printk(KERN_ERR
"hfs: attributes file doesn't exist\n");
379 err
= hfs_find_init(HFSPLUS_SB(dir
->i_sb
)->attr_tree
, &fd
);
384 err
= hfsplus_find_attr(dir
->i_sb
, cnid
, NULL
, &fd
);
387 printk(KERN_ERR
"hfs: xattr search failed.\n");
391 err
= __hfsplus_delete_attr(dir
, cnid
, &fd
);