Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * linux/fs/hfsplus/attributes.c
  3 *
  4 * Vyacheslav Dubeyko <slava@dubeyko.com>
  5 *
  6 * Handling of records in attributes tree
  7 */
  8
  9#include "hfsplus_fs.h"
 10#include "hfsplus_raw.h"
 11
 12static struct kmem_cache *hfsplus_attr_tree_cachep;
 13
 14int __init hfsplus_create_attr_tree_cache(void)
 15{
 16	if (hfsplus_attr_tree_cachep)
 17		return -EEXIST;
 18
 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)
 24		return -ENOMEM;
 25
 26	return 0;
 27}
 28
 29void hfsplus_destroy_attr_tree_cache(void)
 30{
 31	kmem_cache_destroy(hfsplus_attr_tree_cachep);
 32}
 33
 34int hfsplus_attr_bin_cmp_key(const hfsplus_btree_key *k1,
 35				const hfsplus_btree_key *k2)
 36{
 37	__be32 k1_cnid, k2_cnid;
 38
 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;
 43
 44	return hfsplus_strcmp(
 45			(const struct hfsplus_unistr *)&k1->attr.key_name,
 46			(const struct hfsplus_unistr *)&k2->attr.key_name);
 47}
 48
 49int hfsplus_attr_build_key(struct super_block *sb, hfsplus_btree_key *key,
 50			u32 cnid, const char *name)
 51{
 52	int len;
 53
 54	memset(key, 0, sizeof(struct hfsplus_attr_key));
 55	key->attr.cnid = cpu_to_be32(cnid);
 56	if (name) {
 57		int res = hfsplus_asc2uni(sb,
 58				(struct hfsplus_unistr *)&key->attr.key_name,
 59				HFSPLUS_ATTR_MAX_STRLEN, name, strlen(name));
 60		if (res)
 61			return res;
 62		len = be16_to_cpu(key->attr.key_name.length);
 63	} else {
 64		key->attr.key_name.length = 0;
 65		len = 0;
 66	}
 67
 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.
 74	 */
 75	key->key_len =
 76		cpu_to_be16(offsetof(struct hfsplus_attr_key, key_name) +
 77				2 * len);
 78
 79	return 0;
 80}
 81
 82hfsplus_attr_entry *hfsplus_alloc_attr_entry(void)
 83{
 84	return kmem_cache_alloc(hfsplus_attr_tree_cachep, GFP_KERNEL);
 85}
 86
 87void hfsplus_destroy_attr_entry(hfsplus_attr_entry *entry)
 88{
 89	if (entry)
 90		kmem_cache_free(hfsplus_attr_tree_cachep, entry);
 91}
 92
 93#define HFSPLUS_INVALID_ATTR_RECORD -1
 94
 95static int hfsplus_attr_build_record(hfsplus_attr_entry *entry, int record_type,
 96				u32 cnid, const void *value, size_t size)
 97{
 98	if (record_type == HFSPLUS_ATTR_FORK_DATA) {
 99		/*
100		 * Mac OS X supports only inline data attributes.
101		 * Do nothing
102		 */
103		memset(entry, 0, sizeof(*entry));
104		return sizeof(struct hfsplus_attr_fork_data);
105	} else if (record_type == HFSPLUS_ATTR_EXTENTS) {
106		/*
107		 * Mac OS X supports only inline data attributes.
108		 * Do nothing.
109		 */
110		memset(entry, 0, sizeof(*entry));
111		return sizeof(struct hfsplus_attr_extents);
112	} else if (record_type == HFSPLUS_ATTR_INLINE_DATA) {
113		u16 len;
114
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)
118			len = size;
119		else
120			return HFSPLUS_INVALID_ATTR_RECORD;
121		entry->inline_data.length = cpu_to_be16(len);
122		memcpy(entry->inline_data.raw_bytes, value, len);
123		/*
124		 * Align len on two-byte boundary.
125		 * It needs to add pad byte if we have odd len.
126		 */
127		len = round_up(len, 2);
128		return offsetof(struct hfsplus_attr_inline_data, raw_bytes) +
129					len;
130	} else /* invalid input */
131		memset(entry, 0, sizeof(*entry));
132
133	return HFSPLUS_INVALID_ATTR_RECORD;
134}
135
136int hfsplus_find_attr(struct super_block *sb, u32 cnid,
137			const char *name, struct hfs_find_data *fd)
138{
139	int err = 0;
140
141	hfs_dbg(ATTR_MOD, "find_attr: %s,%d\n", name ? name : NULL, cnid);
142
143	if (!HFSPLUS_SB(sb)->attr_tree) {
144		pr_err("attributes file doesn't exist\n");
145		return -EINVAL;
146	}
147
148	if (name) {
149		err = hfsplus_attr_build_key(sb, fd->search_key, cnid, name);
150		if (err)
151			goto failed_find_attr;
152		err = hfs_brec_find(fd, hfs_find_rec_by_key);
153		if (err)
154			goto failed_find_attr;
155	} else {
156		err = hfsplus_attr_build_key(sb, fd->search_key, cnid, NULL);
157		if (err)
158			goto failed_find_attr;
159		err = hfs_brec_find(fd, hfs_find_1st_rec_by_cnid);
160		if (err)
161			goto failed_find_attr;
162	}
163
164failed_find_attr:
165	return err;
166}
167
168int hfsplus_attr_exists(struct inode *inode, const char *name)
169{
170	int err = 0;
171	struct super_block *sb = inode->i_sb;
172	struct hfs_find_data fd;
173
174	if (!HFSPLUS_SB(sb)->attr_tree)
175		return 0;
176
177	err = hfs_find_init(HFSPLUS_SB(sb)->attr_tree, &fd);
178	if (err)
179		return 0;
180
181	err = hfsplus_find_attr(sb, inode->i_ino, name, &fd);
182	if (err)
183		goto attr_not_found;
184
185	hfs_find_exit(&fd);
186	return 1;
187
188attr_not_found:
189	hfs_find_exit(&fd);
190	return 0;
191}
192
193int hfsplus_create_attr(struct inode *inode,
194				const char *name,
195				const void *value, size_t size)
196{
197	struct super_block *sb = inode->i_sb;
198	struct hfs_find_data fd;
199	hfsplus_attr_entry *entry_ptr;
200	int entry_size;
201	int err;
202
203	hfs_dbg(ATTR_MOD, "create_attr: %s,%ld\n",
204		name ? name : NULL, inode->i_ino);
205
206	if (!HFSPLUS_SB(sb)->attr_tree) {
207		pr_err("attributes file doesn't exist\n");
208		return -EINVAL;
209	}
210
211	entry_ptr = hfsplus_alloc_attr_entry();
212	if (!entry_ptr)
213		return -ENOMEM;
214
215	err = hfs_find_init(HFSPLUS_SB(sb)->attr_tree, &fd);
216	if (err)
217		goto failed_init_create_attr;
218
219	if (name) {
220		err = hfsplus_attr_build_key(sb, fd.search_key,
221						inode->i_ino, name);
222		if (err)
223			goto failed_create_attr;
224	} else {
225		err = -EINVAL;
226		goto failed_create_attr;
227	}
228
229	/* Mac OS X supports only inline data attributes. */
230	entry_size = hfsplus_attr_build_record(entry_ptr,
231					HFSPLUS_ATTR_INLINE_DATA,
232					inode->i_ino,
233					value, size);
234	if (entry_size == HFSPLUS_INVALID_ATTR_RECORD) {
235		err = -EINVAL;
236		goto failed_create_attr;
237	}
238
239	err = hfs_brec_find(&fd, hfs_find_rec_by_key);
240	if (err != -ENOENT) {
241		if (!err)
242			err = -EEXIST;
243		goto failed_create_attr;
244	}
245
246	err = hfs_brec_insert(&fd, entry_ptr, entry_size);
247	if (err)
248		goto failed_create_attr;
249
250	hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ATTR_DIRTY);
251
252failed_create_attr:
253	hfs_find_exit(&fd);
254
255failed_init_create_attr:
256	hfsplus_destroy_attr_entry(entry_ptr);
257	return err;
258}
259
260static int __hfsplus_delete_attr(struct inode *inode, u32 cnid,
261					struct hfs_find_data *fd)
262{
263	int err = 0;
264	__be32 found_cnid, record_type;
265
266	hfs_bnode_read(fd->bnode, &found_cnid,
267			fd->keyoffset +
268			offsetof(struct hfsplus_attr_key, cnid),
269			sizeof(__be32));
270	if (cnid != be32_to_cpu(found_cnid))
271		return -ENOENT;
272
273	hfs_bnode_read(fd->bnode, &record_type,
274			fd->entryoffset, sizeof(record_type));
275
276	switch (be32_to_cpu(record_type)) {
277	case HFSPLUS_ATTR_INLINE_DATA:
278		/* All is OK. Do nothing. */
279		break;
280	case HFSPLUS_ATTR_FORK_DATA:
281	case HFSPLUS_ATTR_EXTENTS:
282		pr_err("only inline data xattr are supported\n");
283		return -EOPNOTSUPP;
284	default:
285		pr_err("invalid extended attribute record\n");
286		return -ENOENT;
287	}
288
289	err = hfs_brec_remove(fd);
290	if (err)
291		return err;
292
293	hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ATTR_DIRTY);
294	return err;
295}
296
297int hfsplus_delete_attr(struct inode *inode, const char *name)
298{
299	int err = 0;
300	struct super_block *sb = inode->i_sb;
301	struct hfs_find_data fd;
302
303	hfs_dbg(ATTR_MOD, "delete_attr: %s,%ld\n",
304		name ? name : NULL, inode->i_ino);
305
306	if (!HFSPLUS_SB(sb)->attr_tree) {
307		pr_err("attributes file doesn't exist\n");
308		return -EINVAL;
309	}
310
311	err = hfs_find_init(HFSPLUS_SB(sb)->attr_tree, &fd);
312	if (err)
313		return err;
314
315	if (name) {
316		err = hfsplus_attr_build_key(sb, fd.search_key,
317						inode->i_ino, name);
318		if (err)
319			goto out;
320	} else {
321		pr_err("invalid extended attribute name\n");
322		err = -EINVAL;
323		goto out;
324	}
325
326	err = hfs_brec_find(&fd, hfs_find_rec_by_key);
327	if (err)
328		goto out;
329
330	err = __hfsplus_delete_attr(inode, inode->i_ino, &fd);
331	if (err)
332		goto out;
333
334out:
335	hfs_find_exit(&fd);
336	return err;
337}
338
339int hfsplus_delete_all_attrs(struct inode *dir, u32 cnid)
340{
341	int err = 0;
342	struct hfs_find_data fd;
343
344	hfs_dbg(ATTR_MOD, "delete_all_attrs: %d\n", cnid);
345
346	if (!HFSPLUS_SB(dir->i_sb)->attr_tree) {
347		pr_err("attributes file doesn't exist\n");
348		return -EINVAL;
349	}
350
351	err = hfs_find_init(HFSPLUS_SB(dir->i_sb)->attr_tree, &fd);
352	if (err)
353		return err;
354
355	for (;;) {
356		err = hfsplus_find_attr(dir->i_sb, cnid, NULL, &fd);
357		if (err) {
358			if (err != -ENOENT)
359				pr_err("xattr search failed\n");
360			goto end_delete_all;
361		}
362
363		err = __hfsplus_delete_attr(dir, cnid, &fd);
364		if (err)
365			goto end_delete_all;
366	}
367
368end_delete_all:
369	hfs_find_exit(&fd);
370	return err;
371}