Merge commit 'v3.15' into next
[cascardo/linux.git] / fs / hfsplus / attributes.c
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
12 static struct kmem_cache *hfsplus_attr_tree_cachep;
13
14 int __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
29 void hfsplus_destroy_attr_tree_cache(void)
30 {
31         kmem_cache_destroy(hfsplus_attr_tree_cachep);
32 }
33
34 int 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
49 int 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                 len = strlen(name);
58                 if (len > HFSPLUS_ATTR_MAX_STRLEN) {
59                         pr_err("invalid xattr name's length\n");
60                         return -EINVAL;
61                 }
62                 hfsplus_asc2uni(sb,
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);
66         } else {
67                 key->attr.key_name.length = 0;
68                 len = 0;
69         }
70
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.
77          */
78         key->key_len =
79                 cpu_to_be16(offsetof(struct hfsplus_attr_key, key_name) +
80                                 2 * len);
81
82         return 0;
83 }
84
85 void hfsplus_attr_build_key_uni(hfsplus_btree_key *key,
86                                         u32 cnid,
87                                         struct hfsplus_attr_unistr *name)
88 {
89         int ustrlen;
90
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);
95         ustrlen *= 2;
96         memcpy(key->attr.key_name.unicode, name->unicode, ustrlen);
97
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.
104          */
105         key->key_len =
106                 cpu_to_be16(offsetof(struct hfsplus_attr_key, key_name) +
107                                 ustrlen);
108 }
109
110 hfsplus_attr_entry *hfsplus_alloc_attr_entry(void)
111 {
112         return kmem_cache_alloc(hfsplus_attr_tree_cachep, GFP_KERNEL);
113 }
114
115 void hfsplus_destroy_attr_entry(hfsplus_attr_entry *entry)
116 {
117         if (entry)
118                 kmem_cache_free(hfsplus_attr_tree_cachep, entry);
119 }
120
121 #define HFSPLUS_INVALID_ATTR_RECORD -1
122
123 static int hfsplus_attr_build_record(hfsplus_attr_entry *entry, int record_type,
124                                 u32 cnid, const void *value, size_t size)
125 {
126         if (record_type == HFSPLUS_ATTR_FORK_DATA) {
127                 /*
128                  * Mac OS X supports only inline data attributes.
129                  * Do nothing
130                  */
131                 memset(entry, 0, sizeof(*entry));
132                 return sizeof(struct hfsplus_attr_fork_data);
133         } else if (record_type == HFSPLUS_ATTR_EXTENTS) {
134                 /*
135                  * Mac OS X supports only inline data attributes.
136                  * Do nothing.
137                  */
138                 memset(entry, 0, sizeof(*entry));
139                 return sizeof(struct hfsplus_attr_extents);
140         } else if (record_type == HFSPLUS_ATTR_INLINE_DATA) {
141                 u16 len;
142
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)
146                         len = size;
147                 else
148                         return HFSPLUS_INVALID_ATTR_RECORD;
149                 entry->inline_data.length = cpu_to_be16(len);
150                 memcpy(entry->inline_data.raw_bytes, value, len);
151                 /*
152                  * Align len on two-byte boundary.
153                  * It needs to add pad byte if we have odd len.
154                  */
155                 len = round_up(len, 2);
156                 return offsetof(struct hfsplus_attr_inline_data, raw_bytes) +
157                                         len;
158         } else /* invalid input */
159                 memset(entry, 0, sizeof(*entry));
160
161         return HFSPLUS_INVALID_ATTR_RECORD;
162 }
163
164 int hfsplus_find_attr(struct super_block *sb, u32 cnid,
165                         const char *name, struct hfs_find_data *fd)
166 {
167         int err = 0;
168
169         hfs_dbg(ATTR_MOD, "find_attr: %s,%d\n", name ? name : NULL, cnid);
170
171         if (!HFSPLUS_SB(sb)->attr_tree) {
172                 pr_err("attributes file doesn't exist\n");
173                 return -EINVAL;
174         }
175
176         if (name) {
177                 err = hfsplus_attr_build_key(sb, fd->search_key, cnid, name);
178                 if (err)
179                         goto failed_find_attr;
180                 err = hfs_brec_find(fd, hfs_find_rec_by_key);
181                 if (err)
182                         goto failed_find_attr;
183         } else {
184                 err = hfsplus_attr_build_key(sb, fd->search_key, cnid, NULL);
185                 if (err)
186                         goto failed_find_attr;
187                 err = hfs_brec_find(fd, hfs_find_1st_rec_by_cnid);
188                 if (err)
189                         goto failed_find_attr;
190         }
191
192 failed_find_attr:
193         return err;
194 }
195
196 int hfsplus_attr_exists(struct inode *inode, const char *name)
197 {
198         int err = 0;
199         struct super_block *sb = inode->i_sb;
200         struct hfs_find_data fd;
201
202         if (!HFSPLUS_SB(sb)->attr_tree)
203                 return 0;
204
205         err = hfs_find_init(HFSPLUS_SB(sb)->attr_tree, &fd);
206         if (err)
207                 return 0;
208
209         err = hfsplus_find_attr(sb, inode->i_ino, name, &fd);
210         if (err)
211                 goto attr_not_found;
212
213         hfs_find_exit(&fd);
214         return 1;
215
216 attr_not_found:
217         hfs_find_exit(&fd);
218         return 0;
219 }
220
221 int hfsplus_create_attr(struct inode *inode,
222                                 const char *name,
223                                 const void *value, size_t size)
224 {
225         struct super_block *sb = inode->i_sb;
226         struct hfs_find_data fd;
227         hfsplus_attr_entry *entry_ptr;
228         int entry_size;
229         int err;
230
231         hfs_dbg(ATTR_MOD, "create_attr: %s,%ld\n",
232                 name ? name : NULL, inode->i_ino);
233
234         if (!HFSPLUS_SB(sb)->attr_tree) {
235                 pr_err("attributes file doesn't exist\n");
236                 return -EINVAL;
237         }
238
239         entry_ptr = hfsplus_alloc_attr_entry();
240         if (!entry_ptr)
241                 return -ENOMEM;
242
243         err = hfs_find_init(HFSPLUS_SB(sb)->attr_tree, &fd);
244         if (err)
245                 goto failed_init_create_attr;
246
247         if (name) {
248                 err = hfsplus_attr_build_key(sb, fd.search_key,
249                                                 inode->i_ino, name);
250                 if (err)
251                         goto failed_create_attr;
252         } else {
253                 err = -EINVAL;
254                 goto failed_create_attr;
255         }
256
257         /* Mac OS X supports only inline data attributes. */
258         entry_size = hfsplus_attr_build_record(entry_ptr,
259                                         HFSPLUS_ATTR_INLINE_DATA,
260                                         inode->i_ino,
261                                         value, size);
262         if (entry_size == HFSPLUS_INVALID_ATTR_RECORD) {
263                 err = -EINVAL;
264                 goto failed_create_attr;
265         }
266
267         err = hfs_brec_find(&fd, hfs_find_rec_by_key);
268         if (err != -ENOENT) {
269                 if (!err)
270                         err = -EEXIST;
271                 goto failed_create_attr;
272         }
273
274         err = hfs_brec_insert(&fd, entry_ptr, entry_size);
275         if (err)
276                 goto failed_create_attr;
277
278         hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ATTR_DIRTY);
279
280 failed_create_attr:
281         hfs_find_exit(&fd);
282
283 failed_init_create_attr:
284         hfsplus_destroy_attr_entry(entry_ptr);
285         return err;
286 }
287
288 static int __hfsplus_delete_attr(struct inode *inode, u32 cnid,
289                                         struct hfs_find_data *fd)
290 {
291         int err = 0;
292         __be32 found_cnid, record_type;
293
294         hfs_bnode_read(fd->bnode, &found_cnid,
295                         fd->keyoffset +
296                         offsetof(struct hfsplus_attr_key, cnid),
297                         sizeof(__be32));
298         if (cnid != be32_to_cpu(found_cnid))
299                 return -ENOENT;
300
301         hfs_bnode_read(fd->bnode, &record_type,
302                         fd->entryoffset, sizeof(record_type));
303
304         switch (be32_to_cpu(record_type)) {
305         case HFSPLUS_ATTR_INLINE_DATA:
306                 /* All is OK. Do nothing. */
307                 break;
308         case HFSPLUS_ATTR_FORK_DATA:
309         case HFSPLUS_ATTR_EXTENTS:
310                 pr_err("only inline data xattr are supported\n");
311                 return -EOPNOTSUPP;
312         default:
313                 pr_err("invalid extended attribute record\n");
314                 return -ENOENT;
315         }
316
317         err = hfs_brec_remove(fd);
318         if (err)
319                 return err;
320
321         hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ATTR_DIRTY);
322         return err;
323 }
324
325 int hfsplus_delete_attr(struct inode *inode, const char *name)
326 {
327         int err = 0;
328         struct super_block *sb = inode->i_sb;
329         struct hfs_find_data fd;
330
331         hfs_dbg(ATTR_MOD, "delete_attr: %s,%ld\n",
332                 name ? name : NULL, inode->i_ino);
333
334         if (!HFSPLUS_SB(sb)->attr_tree) {
335                 pr_err("attributes file doesn't exist\n");
336                 return -EINVAL;
337         }
338
339         err = hfs_find_init(HFSPLUS_SB(sb)->attr_tree, &fd);
340         if (err)
341                 return err;
342
343         if (name) {
344                 err = hfsplus_attr_build_key(sb, fd.search_key,
345                                                 inode->i_ino, name);
346                 if (err)
347                         goto out;
348         } else {
349                 pr_err("invalid extended attribute name\n");
350                 err = -EINVAL;
351                 goto out;
352         }
353
354         err = hfs_brec_find(&fd, hfs_find_rec_by_key);
355         if (err)
356                 goto out;
357
358         err = __hfsplus_delete_attr(inode, inode->i_ino, &fd);
359         if (err)
360                 goto out;
361
362 out:
363         hfs_find_exit(&fd);
364         return err;
365 }
366
367 int hfsplus_delete_all_attrs(struct inode *dir, u32 cnid)
368 {
369         int err = 0;
370         struct hfs_find_data fd;
371
372         hfs_dbg(ATTR_MOD, "delete_all_attrs: %d\n", cnid);
373
374         if (!HFSPLUS_SB(dir->i_sb)->attr_tree) {
375                 pr_err("attributes file doesn't exist\n");
376                 return -EINVAL;
377         }
378
379         err = hfs_find_init(HFSPLUS_SB(dir->i_sb)->attr_tree, &fd);
380         if (err)
381                 return err;
382
383         for (;;) {
384                 err = hfsplus_find_attr(dir->i_sb, cnid, NULL, &fd);
385                 if (err) {
386                         if (err != -ENOENT)
387                                 pr_err("xattr search failed\n");
388                         goto end_delete_all;
389                 }
390
391                 err = __hfsplus_delete_attr(dir, cnid, &fd);
392                 if (err)
393                         goto end_delete_all;
394         }
395
396 end_delete_all:
397         hfs_find_exit(&fd);
398         return err;
399 }