444009de9b6deaed63ffb6546de16e67732cd598
[lwext4.git] / lwext4 / ext4_xattr.c
1 /*
2  * Copyright (c) 2015 Grzegorz Kostka (kostka.grzegorz@gmail.com)
3  * Copyright (c) 2015 Kaho Ng (ngkaho1234@gmail.com)
4  *
5  *
6  * HelenOS:
7  * Copyright (c) 2012 Martin Sucha
8  * Copyright (c) 2012 Frantisek Princ
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  *
15  * - Redistributions of source code must retain the above copyright
16  *   notice, this list of conditions and the following disclaimer.
17  * - Redistributions in binary form must reproduce the above copyright
18  *   notice, this list of conditions and the following disclaimer in the
19  *   documentation and/or other materials provided with the distribution.
20  * - The name of the author may not be used to endorse or promote products
21  *   derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34
35 /** @addtogroup lwext4
36  * @{
37  */
38 /**
39  * @file  ext4_xattr.c
40  * @brief Extended Attribute manipulation.
41  */
42
43 #include "ext4_config.h"
44 #include "ext4_types.h"
45 #include "ext4_fs.h"
46 #include "ext4_errno.h"
47 #include "ext4_blockdev.h"
48 #include "ext4_super.h"
49 #include "ext4_debug.h"
50 #include "ext4_block_group.h"
51 #include "ext4_balloc.h"
52 #include "ext4_inode.h"
53 #include "ext4_extent.h"
54
55 #include <string.h>
56 #include <stdlib.h>
57
58 /**
59  * @file  ext4_xattr.c
60  * @brief Extended Attribute Manipulation
61  */
62 static int ext4_xattr_item_cmp(struct ext4_xattr_item *a,
63                                 struct ext4_xattr_item *b)
64 {
65         int result;
66         result = a->name_index - b->name_index;
67         if (result)
68                 return result;
69
70         result = a->name_len - b->name_len;
71         if (result)
72                 return result;
73
74         return memcmp(a->name, b->name, a->name_len);
75 }
76
77 RB_GENERATE_INTERNAL(ext4_xattr_tree,
78                      ext4_xattr_item,
79                      node,
80                      ext4_xattr_item_cmp,
81                      static inline)
82
83 static struct ext4_xattr_item *
84 ext4_xattr_item_alloc(uint8_t name_index,
85                       char   *name,
86                       size_t  name_len)
87 {
88         struct ext4_xattr_item *item;
89         item = malloc(sizeof(struct ext4_xattr_item) +
90                         name_len);
91         if (!item)
92                 return NULL;
93
94         item->name_index = name_index;
95         item->name       = (char *)(item + 1);
96         item->name_len   = name_len;
97         item->data       = NULL;
98         item->data_size  = 0;
99         
100         memset(&item->node, 0, sizeof(item->node));
101         memcpy(item->name, name, name_len);
102
103         return item;
104 }
105
106 static int
107 ext4_xattr_item_alloc_data(struct ext4_xattr_item *item,
108                            void *orig_data,
109                            size_t data_size)
110 {
111         void *data = NULL;
112         ext4_assert(!item->data);
113         data = malloc(data_size);
114         if (!data)
115                 return ENOMEM;
116
117         if (orig_data)
118                 memcpy(data, orig_data, data_size);
119
120         item->data = data;
121         item->data_size = data_size;
122         return EOK;
123 }
124
125 static void
126 ext4_xattr_item_free_data(struct ext4_xattr_item *item)
127 {
128         ext4_assert(item->data);
129         free(item->data);
130         item->data = NULL;
131         item->data_size = 0;
132 }
133
134 static int
135 ext4_xattr_item_resize_data(struct ext4_xattr_item *item,
136                             size_t new_data_size)
137 {
138         if (new_data_size != item->data_size) {
139                 void *new_data;
140                 new_data = realloc(item->data, new_data_size);
141                 if (!new_data)
142                         return ENOMEM;
143
144                 item->data = new_data;
145                 item->data_size = new_data_size;
146         }
147         return EOK;
148 }
149
150 static void
151 ext4_xattr_item_free(struct ext4_xattr_item *item)
152 {
153         if (item->data)
154                 ext4_xattr_item_free_data(item);
155
156         free(item);
157 }
158
159
160 static void *ext4_xattr_entry_data(struct ext4_xattr_ref *xattr_ref,
161                                    struct ext4_xattr_entry *entry,
162                                    bool in_inode)
163 {
164         void *ret;
165         if (in_inode) {
166                 struct ext4_xattr_ibody_header *header;
167                 struct ext4_xattr_entry *first_entry;
168                 uint16_t inode_size = ext4_get16(&xattr_ref->fs->sb,
169                                                  inode_size);
170                 header = EXT4_XATTR_IHDR(xattr_ref->inode_ref->inode);
171                 first_entry = EXT4_XATTR_IFIRST(header);
172  
173                 ret = (void *)((char *)first_entry + to_le16(entry->e_value_offs));
174                 if ((char *)ret + EXT4_XATTR_SIZE(to_le32(entry->e_value_size))
175                         - (char *)xattr_ref->inode_ref->inode >
176                     inode_size)
177                         ret = NULL;
178
179         } else {
180                 uint32_t block_size =
181                                 ext4_sb_get_block_size(&xattr_ref->fs->sb);
182                 ret = (void *)((char *)xattr_ref->block.data + 
183                                 to_le16(entry->e_value_offs));
184                 if ((char *)ret + EXT4_XATTR_SIZE(to_le32(entry->e_value_size))
185                         - (char *)xattr_ref->block.data >
186                     block_size)
187                         ret = NULL;
188         }
189         return ret;
190 }
191
192 static int ext4_xattr_block_fetch(struct ext4_xattr_ref *xattr_ref)
193 {
194         int ret = EOK;
195         size_t size_rem;
196         void *data;
197         struct ext4_xattr_entry *entry = NULL;
198
199         ext4_assert(xattr_ref->block.data);
200         entry = EXT4_XATTR_BFIRST(&xattr_ref->block);
201
202         size_rem = ext4_sb_get_block_size(&xattr_ref->fs->sb);
203         for(;size_rem > 0 && !EXT4_XATTR_IS_LAST_ENTRY(entry);
204             entry = EXT4_XATTR_NEXT(entry),
205             size_rem -= EXT4_XATTR_LEN(entry->e_name_len)) {
206                 struct ext4_xattr_item *item;
207                 char *e_name = (char *)(entry + 1);
208
209                 data = ext4_xattr_entry_data(xattr_ref, entry,
210                                              false);
211                 if (!data) {
212                         ret = EIO;
213                         goto Finish;
214                 }
215
216                 item = ext4_xattr_item_alloc(entry->e_name_index,
217                                              e_name,
218                                              (size_t)entry->e_name_len);
219                 if (!item) {
220                         ret = ENOMEM;
221                         goto Finish;
222                 }
223                 if (ext4_xattr_item_alloc_data(item,
224                                                data,
225                                                to_le32(entry->e_value_size))
226                         != EOK) {
227                         ext4_xattr_item_free(item);
228                         ret = ENOMEM;
229                         goto Finish;
230                 }
231                 RB_INSERT(ext4_xattr_tree, &xattr_ref->root, item);
232                 xattr_ref->ea_size += item->data_size;
233         }
234
235 Finish:
236         return ret;
237 }
238
239 static int ext4_xattr_inode_fetch(struct ext4_xattr_ref *xattr_ref)
240 {
241         void *data;
242         size_t size_rem;
243         int ret = EOK;
244         struct ext4_xattr_ibody_header *header = NULL;
245         struct ext4_xattr_entry *entry = NULL;
246         uint16_t inode_size = ext4_get16(&xattr_ref->fs->sb,
247                                          inode_size);
248
249         header = EXT4_XATTR_IHDR(xattr_ref->inode_ref->inode);
250         entry = EXT4_XATTR_IFIRST(header);
251
252         size_rem = inode_size -
253                 EXT4_GOOD_OLD_INODE_SIZE -
254                 xattr_ref->inode_ref->inode->extra_isize;
255         for(;size_rem > 0 && !EXT4_XATTR_IS_LAST_ENTRY(entry);
256             entry = EXT4_XATTR_NEXT(entry),
257             size_rem -= EXT4_XATTR_LEN(entry->e_name_len)) {
258                 struct ext4_xattr_item *item;
259                 char *e_name = (char *)(entry + 1);
260
261                 data = ext4_xattr_entry_data(xattr_ref, entry,
262                                              true);
263                 if (!data) {
264                         ret = EIO;
265                         goto Finish;
266                 }
267
268                 item = ext4_xattr_item_alloc(entry->e_name_index,
269                                              e_name,
270                                              (size_t)entry->e_name_len);
271                 if (!item) {
272                         ret = ENOMEM;
273                         goto Finish;
274                 }
275                 if (ext4_xattr_item_alloc_data(item,
276                                                data,
277                                                to_le32(entry->e_value_size))
278                         != EOK) {
279                         ext4_xattr_item_free(item);
280                         ret = ENOMEM;
281                         goto Finish;
282                 }
283                 RB_INSERT(ext4_xattr_tree, &xattr_ref->root, item);
284                 xattr_ref->ea_size += item->data_size;
285         }
286
287 Finish:
288         return ret;
289 }
290
291 static int ext4_xattr_fetch(struct ext4_xattr_ref *xattr_ref)
292 {
293         int ret = EOK;
294         uint16_t inode_size = ext4_get16(&xattr_ref->fs->sb,
295                                          inode_size);
296         if (inode_size > EXT4_GOOD_OLD_INODE_SIZE) {
297                 ret = ext4_xattr_inode_fetch(xattr_ref);
298                 if (ret != EOK)
299                         return ret;
300
301         }
302
303         if (xattr_ref->block_loaded)
304                 ret = ext4_xattr_block_fetch(xattr_ref);
305
306         xattr_ref->dirty = false;
307         return ret;
308 }
309
310 static struct ext4_xattr_item *
311 ext4_xattr_lookup_item(struct ext4_xattr_ref *xattr_ref,
312                        uint8_t name_index,
313                        char   *name,
314                        size_t  name_len)
315 {
316         struct ext4_xattr_item tmp, *ret;
317         tmp.name_index = name_index;
318         tmp.name       = name;
319         tmp.name_len   = name_len;
320         ret = RB_FIND(ext4_xattr_tree, &xattr_ref->root,
321                         &tmp);
322         return ret;
323 }
324
325 static void
326 ext4_xattr_purge_items(struct ext4_xattr_ref *xattr_ref)
327 {
328         struct ext4_xattr_item *item, *save_item;
329         RB_FOREACH_SAFE(item,
330                         ext4_xattr_tree,
331                         &xattr_ref->root,
332                         save_item) {
333                 RB_REMOVE(ext4_xattr_tree, &xattr_ref->root, item);
334                 ext4_xattr_item_free(item);
335         }
336         xattr_ref->ea_size = 0;
337 }
338
339 int ext4_fs_get_xattr_ref(struct ext4_fs *fs,
340                           struct ext4_inode_ref *inode_ref,
341                           struct ext4_xattr_ref *ref)
342 {
343         int rc;
344         uint64_t xattr_block;
345         xattr_block = ext4_inode_get_file_acl(inode_ref->inode,
346                                               &fs->sb);
347         RB_INIT(&ref->root);
348         if (xattr_block) {
349                 rc = ext4_block_get(fs->bdev,
350                                     &inode_ref->block, xattr_block);
351                 if (rc != EOK)
352                         return EIO;
353         
354                 ref->block_loaded = true;
355         } else
356                 ref->block_loaded = false;
357
358         ref->inode_ref = inode_ref;
359         ref->fs = fs;
360
361         rc = ext4_xattr_fetch(ref);
362         if (rc != EOK) {
363                 ext4_xattr_purge_items(ref);
364                 if (xattr_block)
365                         ext4_block_set(fs->bdev, &inode_ref->block);
366
367                 ref->block_loaded = false;
368                 return rc;
369         }
370         return EOK;
371 }
372
373 void ext4_fs_put_xattr_ref(struct ext4_xattr_ref *ref)
374 {
375         if (ref->block_loaded) {
376                 ext4_block_set(ref->fs->bdev, &ref->block);
377                 ref->block_loaded = false;
378         }
379         ext4_xattr_purge_items(ref);
380         ref->inode_ref = NULL;
381         ref->fs = NULL;
382 }
383
384 /**
385  * @}
386  */