Pedanitic warning check fixes. Pointer arithmetic, anonymus unions etc
[lwext4.git] / lwext4 / ext4_dir_idx.c
1 /*\r
2  * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)\r
3  * All rights reserved.\r
4  *\r
5  * Redistribution and use in source and binary forms, with or without\r
6  * modification, are permitted provided that the following conditions\r
7  * are met:\r
8  *\r
9  * - Redistributions of source code must retain the above copyright\r
10  *   notice, this list of conditions and the following disclaimer.\r
11  * - Redistributions in binary form must reproduce the above copyright\r
12  *   notice, this list of conditions and the following disclaimer in the\r
13  *   documentation and/or other materials provided with the distribution.\r
14  * - The name of the author may not be used to endorse or promote products\r
15  *   derived from this software without specific prior written permission.\r
16  *\r
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\r
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\r
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\r
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\r
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\r
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\r
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
27  */\r
28 \r
29 /** @addtogroup lwext4\r
30  * @{\r
31  */\r
32 /**\r
33  * @file  ext4_dir_idx.c\r
34  * @brief Directory indexing procedures.\r
35  */\r
36 \r
37 #include <ext4_config.h>\r
38 #include <ext4_dir_idx.h>\r
39 #include <ext4_dir.h>\r
40 #include <ext4_blockdev.h>\r
41 #include <ext4_fs.h>\r
42 #include <ext4_super.h>\r
43 #include <ext4_hash.h>\r
44 \r
45 #include <string.h>\r
46 #include <stdlib.h>\r
47 \r
48 /**@brief Sort entry item.*/\r
49 struct ext4_dx_sort_entry {\r
50     uint32_t hash;\r
51     uint32_t rec_len;\r
52     void *dentry;\r
53 };\r
54 \r
55 \r
56 static int ext4_dir_dx_hash_string(struct ext4_hash_info *hinfo, int len,\r
57     const char *name)\r
58 {\r
59     return ext2_htree_hash(name, len, hinfo->seed, hinfo->hash_version,\r
60             &hinfo->hash, &hinfo->minor_hash);\r
61 }\r
62 \r
63 \r
64 uint8_t ext4_dir_dx_root_info_get_hash_version(\r
65     struct ext4_directory_dx_root_info *root_info)\r
66 {\r
67     return root_info->hash_version;\r
68 }\r
69 \r
70 \r
71 void ext4_dir_dx_root_info_set_hash_version(\r
72     struct ext4_directory_dx_root_info  *root_info, uint8_t v)\r
73 {\r
74     root_info->hash_version = v;\r
75 }\r
76 \r
77 uint8_t ext4_dir_dx_root_info_get_info_length(\r
78     struct ext4_directory_dx_root_info *root_info)\r
79 {\r
80     return root_info->info_length;\r
81 }\r
82 void ext4_dir_dx_root_info_set_info_length(\r
83     struct ext4_directory_dx_root_info  *root_info, uint8_t len)\r
84 {\r
85     root_info->info_length = len;\r
86 }\r
87 \r
88 uint8_t ext4_dir_dx_root_info_get_indirect_levels(\r
89     struct ext4_directory_dx_root_info *root_info)\r
90 {\r
91     return root_info->indirect_levels;\r
92 }\r
93 \r
94 void ext4_dir_dx_root_info_set_indirect_levels(\r
95     struct ext4_directory_dx_root_info *root_info, uint8_t lvl)\r
96 {\r
97     root_info->indirect_levels = lvl;\r
98 }\r
99 \r
100 \r
101 \r
102 uint16_t ext4_dir_dx_countlimit_get_limit(\r
103     struct ext4_directory_dx_countlimit *climit)\r
104 {\r
105     return to_le16(climit->limit);\r
106 }\r
107 void ext4_dir_dx_countlimit_set_limit(\r
108     struct ext4_directory_dx_countlimit *climit, uint16_t limit)\r
109 {\r
110     climit->limit = to_le16(limit);\r
111 }\r
112 \r
113 uint16_t ext4_dir_dx_countlimit_get_count(\r
114     struct ext4_directory_dx_countlimit *climit)\r
115 {\r
116     return to_le16(climit->count);\r
117 }\r
118 \r
119 void ext4_dir_dx_countlimit_set_count(\r
120     struct ext4_directory_dx_countlimit *climit, uint16_t count)\r
121 {\r
122     climit->count = to_le16(count);\r
123 }\r
124 \r
125 \r
126 uint32_t ext4_dir_dx_entry_get_hash(\r
127     struct ext4_directory_dx_entry *entry)\r
128 {\r
129     return to_le32(entry->hash);\r
130 }\r
131 void ext4_dir_dx_entry_set_hash(\r
132     struct ext4_directory_dx_entry *entry, uint32_t hash)\r
133 {\r
134     entry->hash = to_le32(hash);\r
135 }\r
136 \r
137 uint32_t ext4_dir_dx_entry_get_block(\r
138     struct ext4_directory_dx_entry *entry)\r
139 {\r
140     return to_le32(entry->block);\r
141 }\r
142 void ext4_dir_dx_entry_set_block(\r
143     struct ext4_directory_dx_entry *entry, uint32_t block)\r
144 {\r
145     entry->block = to_le32(block);\r
146 }\r
147 /****************************************************************************/\r
148 \r
149 int ext4_dir_dx_init(struct ext4_inode_ref *dir)\r
150 {\r
151     /* Load block 0, where will be index root located */\r
152     uint32_t fblock;\r
153     int rc = ext4_fs_get_inode_data_block_index(dir, 0,\r
154             &fblock);\r
155     if (rc != EOK)\r
156         return rc;\r
157 \r
158     struct ext4_block block;\r
159     rc = ext4_block_get(dir->fs->bdev, &block, fblock);\r
160     if (rc != EOK)\r
161         return rc;\r
162 \r
163     /* Initialize pointers to data structures */\r
164     struct ext4_directory_dx_root *root = (void *)block.data;\r
165     struct ext4_directory_dx_root_info *info = &(root->info);\r
166 \r
167     /* Initialize root info structure */\r
168     uint8_t hash_version = ext4_get8(&dir->fs->sb, default_hash_version);\r
169 \r
170     ext4_dir_dx_root_info_set_hash_version(info, hash_version);\r
171     ext4_dir_dx_root_info_set_indirect_levels(info, 0);\r
172     ext4_dir_dx_root_info_set_info_length(info, 8);\r
173 \r
174     /* Set limit and current number of entries */\r
175     struct ext4_directory_dx_countlimit *countlimit =\r
176             (struct ext4_directory_dx_countlimit *) &root->entries;\r
177 \r
178     ext4_dir_dx_countlimit_set_count(countlimit, 1);\r
179 \r
180     uint32_t block_size =\r
181             ext4_sb_get_block_size(&dir->fs->sb);\r
182     uint32_t entry_space =\r
183             block_size - 2 * sizeof(struct ext4_directory_dx_dot_entry) -\r
184             sizeof(struct ext4_directory_dx_root_info);\r
185     uint16_t root_limit = entry_space / sizeof(struct ext4_directory_dx_entry);\r
186     ext4_dir_dx_countlimit_set_limit(countlimit, root_limit);\r
187 \r
188     /* Append new block, where will be new entries inserted in the future */\r
189     uint32_t iblock;\r
190     rc = ext4_fs_append_inode_block(dir, &fblock, &iblock);\r
191     if (rc != EOK) {\r
192         ext4_block_set(dir->fs->bdev, &block);\r
193         return rc;\r
194     }\r
195 \r
196     struct ext4_block new_block;\r
197 \r
198     rc = ext4_block_get(dir->fs->bdev, &new_block, fblock);\r
199     if (rc != EOK) {\r
200         ext4_block_set(dir->fs->bdev, &block);\r
201         return rc;\r
202     }\r
203 \r
204     /* Fill the whole block with empty entry */\r
205     struct ext4_directory_entry_ll *block_entry = (void *)new_block.data;\r
206 \r
207     ext4_dir_entry_ll_set_entry_length(block_entry, block_size);\r
208     ext4_dir_entry_ll_set_inode(block_entry, 0);\r
209 \r
210     new_block.dirty = true;\r
211     rc = ext4_block_set(dir->fs->bdev, &new_block);\r
212     if (rc != EOK) {\r
213         ext4_block_set(dir->fs->bdev, &block);\r
214         return rc;\r
215     }\r
216 \r
217     /* Connect new block to the only entry in index */\r
218     struct ext4_directory_dx_entry *entry = root->entries;\r
219     ext4_dir_dx_entry_set_block(entry, iblock);\r
220 \r
221     block.dirty = true;\r
222 \r
223     return ext4_block_set(dir->fs->bdev, &block);\r
224 }\r
225 \r
226 /**@brief Initialize hash info structure necessary for index operations.\r
227  * @param hinfo      Pointer to hinfo to be initialized\r
228  * @param root_block Root block (number 0) of index\r
229  * @param sb         Pointer to superblock\r
230  * @param name_len   Length of name to be computed hash value from\r
231  * @param name       Name to be computed hash value from\r
232  * @return Standard error code\r
233  */\r
234 static int ext4_dir_hinfo_init(struct ext4_hash_info *hinfo,\r
235     struct ext4_block *root_block, struct ext4_sblock *sb, size_t name_len,\r
236     const char *name)\r
237 {\r
238     struct ext4_directory_dx_root *root =\r
239             (struct ext4_directory_dx_root *) root_block->data;\r
240 \r
241     if ((root->info.hash_version != EXT2_HTREE_LEGACY) &&\r
242             (root->info.hash_version != EXT2_HTREE_HALF_MD4) &&\r
243             (root->info.hash_version != EXT2_HTREE_TEA))\r
244         return EXT4_ERR_BAD_DX_DIR;\r
245 \r
246     /* Check unused flags */\r
247     if (root->info.unused_flags != 0)\r
248         return EXT4_ERR_BAD_DX_DIR;\r
249 \r
250     /* Check indirect levels */\r
251     if (root->info.indirect_levels > 1)\r
252         return EXT4_ERR_BAD_DX_DIR;\r
253 \r
254     /* Check if node limit is correct */\r
255     uint32_t block_size = ext4_sb_get_block_size(sb);\r
256     uint32_t entry_space = block_size;\r
257     entry_space -= 2 * sizeof(struct ext4_directory_dx_dot_entry);\r
258     entry_space -= sizeof(struct ext4_directory_dx_root_info);\r
259     entry_space = entry_space / sizeof(struct ext4_directory_dx_entry);\r
260 \r
261     uint16_t limit = ext4_dir_dx_countlimit_get_limit(\r
262             (struct ext4_directory_dx_countlimit *) &root->entries);\r
263     if (limit != entry_space)\r
264         return EXT4_ERR_BAD_DX_DIR;\r
265 \r
266     /* Check hash version and modify if necessary */\r
267     hinfo->hash_version =\r
268             ext4_dir_dx_root_info_get_hash_version(&root->info);\r
269     if ((hinfo->hash_version <= EXT2_HTREE_TEA) &&\r
270             (ext4_sb_check_flag(sb, EXT4_SUPERBLOCK_FLAGS_UNSIGNED_HASH))) {\r
271         /* Use unsigned hash */\r
272         hinfo->hash_version += 3;\r
273     }\r
274 \r
275     /* Load hash seed from superblock */\r
276 \r
277     hinfo->seed = ext4_get8(sb, hash_seed);\r
278 \r
279     /* Compute hash value of name */\r
280     if (name)\r
281         return ext4_dir_dx_hash_string(hinfo, name_len, name);\r
282 \r
283     return EOK;\r
284 }\r
285 \r
286 /**@brief Walk through index tree and load leaf with corresponding hash value.\r
287  * @param hinfo      Initialized hash info structure\r
288  * @param inode_ref  Current i-node\r
289  * @param root_block Root block (iblock 0), where is root node located\r
290  * @param dx_block   Pointer to leaf node in dx_blocks array\r
291  * @param dx_blocks  Array with the whole path from root to leaf\r
292  * @return Standard error code\r
293  */\r
294 static int ext4_dir_dx_get_leaf(struct ext4_hash_info *hinfo,\r
295     struct ext4_inode_ref *inode_ref, struct ext4_block *root_block,\r
296     struct ext4_directory_dx_block **dx_block,\r
297     struct ext4_directory_dx_block *dx_blocks)\r
298 {\r
299     struct ext4_directory_dx_block *tmp_dx_block = dx_blocks;\r
300     struct ext4_directory_dx_root *root =\r
301             (struct ext4_directory_dx_root *) root_block->data;\r
302     struct ext4_directory_dx_entry *entries =\r
303             (struct ext4_directory_dx_entry *) &root->entries;\r
304 \r
305     uint16_t limit = ext4_dir_dx_countlimit_get_limit(\r
306             (struct ext4_directory_dx_countlimit *) entries);\r
307     uint8_t indirect_level =\r
308             ext4_dir_dx_root_info_get_indirect_levels(&root->info);\r
309 \r
310     struct ext4_block *tmp_block = root_block;\r
311     struct ext4_directory_dx_entry *p;\r
312     struct ext4_directory_dx_entry *q;\r
313     struct ext4_directory_dx_entry *m;\r
314     struct ext4_directory_dx_entry *at;\r
315 \r
316     /* Walk through the index tree */\r
317     while (true) {\r
318         uint16_t count = ext4_dir_dx_countlimit_get_count(\r
319                 (struct ext4_directory_dx_countlimit *) entries);\r
320         if ((count == 0) || (count > limit))\r
321             return EXT4_ERR_BAD_DX_DIR;\r
322 \r
323         /* Do binary search in every node */\r
324         p = entries + 1;\r
325         q = entries + count - 1;\r
326 \r
327         while (p <= q) {\r
328             m = p + (q - p) / 2;\r
329             if (ext4_dir_dx_entry_get_hash(m) > hinfo->hash)\r
330                 q = m - 1;\r
331             else\r
332                 p = m + 1;\r
333         }\r
334 \r
335         at = p - 1;\r
336 \r
337         /* Write results */\r
338 \r
339         memcpy(&tmp_dx_block->block, tmp_block, sizeof(struct ext4_block));\r
340         tmp_dx_block->entries = entries;\r
341         tmp_dx_block->position = at;\r
342 \r
343         /* Is algorithm in the leaf? */\r
344         if (indirect_level == 0) {\r
345             *dx_block = tmp_dx_block;\r
346             return EOK;\r
347         }\r
348 \r
349         /* Goto child node */\r
350         uint32_t next_block = ext4_dir_dx_entry_get_block(at);\r
351 \r
352         indirect_level--;\r
353 \r
354         uint32_t fblock;\r
355         int rc = ext4_fs_get_inode_data_block_index(inode_ref,\r
356                 next_block, &fblock);\r
357         if (rc != EOK)\r
358             return rc;\r
359 \r
360         rc = ext4_block_get(inode_ref->fs->bdev, tmp_block, fblock);\r
361         if (rc != EOK)\r
362             return rc;\r
363 \r
364         entries = ((struct ext4_directory_dx_node *) tmp_block->data)->entries;\r
365         limit = ext4_dir_dx_countlimit_get_limit(\r
366                 (struct ext4_directory_dx_countlimit *) entries);\r
367 \r
368         uint16_t entry_space =\r
369                 ext4_sb_get_block_size(&inode_ref->fs->sb) -\r
370                 sizeof(struct ext4_fake_directory_entry);\r
371 \r
372         entry_space = entry_space / sizeof(struct ext4_directory_dx_entry);\r
373 \r
374         if (limit != entry_space) {\r
375             ext4_block_set(inode_ref->fs->bdev, tmp_block);\r
376             return EXT4_ERR_BAD_DX_DIR;\r
377         }\r
378 \r
379         ++tmp_dx_block;\r
380     }\r
381 \r
382     /* Unreachable */\r
383     return EOK;\r
384 }\r
385 \r
386 /**@brief Check if the the next block would be checked during entry search.\r
387  * @param inode_ref Directory i-node\r
388  * @param hash      Hash value to check\r
389  * @param dx_block  Current block\r
390  * @param dx_blocks Array with path from root to leaf node\r
391  * @return Standard Error codee\r
392  */\r
393 static int ext4_dir_dx_next_block(struct ext4_inode_ref *inode_ref,\r
394     uint32_t hash, struct ext4_directory_dx_block *dx_block,\r
395     struct ext4_directory_dx_block *dx_blocks)\r
396 {\r
397     uint32_t num_handles = 0;\r
398     struct ext4_directory_dx_block *p = dx_block;\r
399 \r
400     /* Try to find data block with next bunch of entries */\r
401     while (true) {\r
402         p->position++;\r
403         uint16_t count = ext4_dir_dx_countlimit_get_count(\r
404                 (struct ext4_directory_dx_countlimit *) p->entries);\r
405 \r
406         if (p->position < p->entries + count)\r
407             break;\r
408 \r
409         if (p == dx_blocks)\r
410             return EOK;\r
411 \r
412         num_handles++;\r
413         p--;\r
414     }\r
415 \r
416     /* Check hash collision (if not occured - no next block cannot be used)*/\r
417     uint32_t current_hash = ext4_dir_dx_entry_get_hash(p->position);\r
418     if ((hash & 1) == 0) {\r
419         if ((current_hash & ~1) != hash)\r
420             return 0;\r
421     }\r
422 \r
423     /* Fill new path */\r
424     while (num_handles--) {\r
425         uint32_t block_idx =\r
426                 ext4_dir_dx_entry_get_block(p->position);\r
427         uint32_t block_addr;\r
428 \r
429         int rc = ext4_fs_get_inode_data_block_index(inode_ref,\r
430                 block_idx, &block_addr);\r
431         if (rc != EOK)\r
432             return rc;\r
433 \r
434 \r
435         struct ext4_block block;\r
436         rc = ext4_block_get(inode_ref->fs->bdev, &block, block_addr);\r
437         if (rc != EOK)\r
438             return rc;\r
439 \r
440         p++;\r
441 \r
442         /* Don't forget to put old block (prevent memory leak) */\r
443         ext4_block_set(inode_ref->fs->bdev, &p->block);\r
444 \r
445 \r
446 \r
447         memcpy(&p->block, &p->block, sizeof(block));\r
448         p->entries = ((struct ext4_directory_dx_node *) block.data)->entries;\r
449         p->position = p->entries;\r
450     }\r
451 \r
452     return ENOENT;\r
453 }\r
454 \r
455 \r
456 \r
457 int ext4_dir_dx_find_entry(struct ext4_directory_search_result * result,\r
458     struct ext4_inode_ref *inode_ref, size_t name_len, const char *name)\r
459 {\r
460     /* Load direct block 0 (index root) */\r
461     uint32_t root_block_addr;\r
462     int rc = ext4_fs_get_inode_data_block_index(inode_ref, 0,\r
463             &root_block_addr);\r
464     if (rc != EOK)\r
465         return rc;\r
466 \r
467     struct ext4_fs *fs = inode_ref->fs;\r
468 \r
469     struct ext4_block root_block;\r
470     rc = ext4_block_get(fs->bdev, &root_block, root_block_addr);\r
471     if (rc != EOK)\r
472         return rc;\r
473 \r
474     /* Initialize hash info (compute hash value) */\r
475     struct ext4_hash_info hinfo;\r
476     rc = ext4_dir_hinfo_init(&hinfo, &root_block, &fs->sb,\r
477             name_len, name);\r
478     if (rc != EOK) {\r
479         ext4_block_set(fs->bdev, &root_block);\r
480         return EXT4_ERR_BAD_DX_DIR;\r
481     }\r
482 \r
483     /*\r
484      * Hardcoded number 2 means maximum height of index tree,\r
485      * specified in the Linux driver.\r
486      */\r
487     struct ext4_directory_dx_block dx_blocks[2];\r
488     struct ext4_directory_dx_block *dx_block;\r
489     struct ext4_directory_dx_block *tmp;\r
490 \r
491     rc = ext4_dir_dx_get_leaf(&hinfo, inode_ref, &root_block,\r
492             &dx_block, dx_blocks);\r
493     if (rc != EOK) {\r
494         ext4_block_set(fs->bdev, &root_block);\r
495         return EXT4_ERR_BAD_DX_DIR;\r
496     }\r
497 \r
498     do {\r
499         /* Load leaf block */\r
500         uint32_t leaf_block_idx =\r
501                 ext4_dir_dx_entry_get_block(dx_block->position);\r
502         uint32_t leaf_block_addr;\r
503 \r
504         rc = ext4_fs_get_inode_data_block_index(inode_ref,\r
505                 leaf_block_idx, &leaf_block_addr);\r
506         if (rc != EOK)\r
507             goto cleanup;\r
508 \r
509         struct ext4_block leaf_block;\r
510         rc = ext4_block_get(fs->bdev, &leaf_block, leaf_block_addr);\r
511         if (rc != EOK)\r
512             goto cleanup;\r
513 \r
514         /* Linear search inside block */\r
515         struct ext4_directory_entry_ll *res_dentry;\r
516         rc = ext4_dir_find_in_block(&leaf_block, &fs->sb,\r
517                 name_len, name, &res_dentry);\r
518 \r
519         /* Found => return it */\r
520         if (rc == EOK) {\r
521             result->block = leaf_block;\r
522             result->dentry = res_dentry;\r
523             goto cleanup;\r
524         }\r
525 \r
526         /* Not found, leave untouched */\r
527         ext4_block_set(fs->bdev, &leaf_block);\r
528 \r
529         if (rc != ENOENT)\r
530             goto cleanup;\r
531 \r
532         /* check if the next block could be checked */\r
533         rc = ext4_dir_dx_next_block(inode_ref, hinfo.hash,\r
534                 dx_block, &dx_blocks[0]);\r
535         if (rc < 0)\r
536             goto cleanup;\r
537     } while (rc == ENOENT);\r
538 \r
539     /* Entry not found */\r
540     rc = ENOENT;\r
541 \r
542     cleanup:\r
543     /* The whole path must be released (preventing memory leak) */\r
544     tmp = dx_blocks;\r
545 \r
546     while (tmp <= dx_block) {\r
547         ext4_block_set(fs->bdev, &tmp->block);\r
548         ++tmp;\r
549     }\r
550 \r
551     return rc;\r
552 }\r
553 \r
554 \r
555 /**@brief  Compare function used to pass in quicksort implementation.\r
556  *         It can compare two entries by hash value.\r
557  * @param arg1  First entry\r
558  * @param arg2  Second entry\r
559  * @param dummy Unused parameter, can be NULL\r
560  *\r
561  * @return Classic compare result\r
562  *         (0: equal, -1: arg1 < arg2, 1: arg1 > arg2)\r
563  */\r
564 static int ext4_dir_dx_entry_comparator(const void *arg1, const void *arg2)\r
565 {\r
566     struct ext4_dx_sort_entry *entry1 = (void *)arg1;\r
567     struct ext4_dx_sort_entry *entry2 = (void *)arg2;\r
568 \r
569     if (entry1->hash == entry2->hash)\r
570         return 0;\r
571 \r
572     if (entry1->hash < entry2->hash)\r
573         return -1;\r
574     else\r
575         return 1;\r
576 }\r
577 \r
578 \r
579 /**@brief  Insert new index entry to block.\r
580  *         Note that space for new entry must be checked by caller.\r
581  * @param index_block Block where to insert new entry\r
582  * @param hash        Hash value covered by child node\r
583  * @param iblock      Logical number of child block\r
584  *\r
585  */\r
586 static void ext4_dir_dx_insert_entry(\r
587     struct ext4_directory_dx_block *index_block, uint32_t hash,\r
588     uint32_t iblock)\r
589 {\r
590     struct ext4_directory_dx_entry *old_index_entry = index_block->position;\r
591     struct ext4_directory_dx_entry *new_index_entry = old_index_entry + 1;\r
592 \r
593     struct ext4_directory_dx_countlimit *countlimit =\r
594             (struct ext4_directory_dx_countlimit *) index_block->entries;\r
595     uint32_t count = ext4_dir_dx_countlimit_get_count(countlimit);\r
596 \r
597     struct ext4_directory_dx_entry *start_index = index_block->entries;\r
598     size_t bytes = (uint8_t *) (start_index + count) - (uint8_t *) (new_index_entry);\r
599 \r
600     memmove(new_index_entry + 1, new_index_entry, bytes);\r
601 \r
602     ext4_dir_dx_entry_set_block(new_index_entry, iblock);\r
603     ext4_dir_dx_entry_set_hash(new_index_entry, hash);\r
604 \r
605     ext4_dir_dx_countlimit_set_count(countlimit, count + 1);\r
606 \r
607     index_block->block.dirty = true;\r
608 }\r
609 \r
610 /**@brief Split directory entries to two parts preventing node overflow.\r
611  * @param inode_ref      Directory i-node\r
612  * @param hinfo          Hash info\r
613  * @param old_data_block Block with data to be split\r
614  * @param index_block    Block where index entries are located\r
615  * @param new_data_block Output value for newly allocated data block\r
616  */\r
617 static int ext4_dir_dx_split_data(struct ext4_inode_ref *inode_ref,\r
618     struct ext4_hash_info *hinfo, struct ext4_block *old_data_block,\r
619     struct ext4_directory_dx_block *index_block,\r
620     struct ext4_block *new_data_block)\r
621 {\r
622     int rc = EOK;\r
623 \r
624     /* Allocate buffer for directory entries */\r
625     uint32_t block_size =\r
626             ext4_sb_get_block_size(&inode_ref->fs->sb);\r
627 \r
628     uint8_t *entry_buffer = malloc(block_size);\r
629     if (entry_buffer == NULL)\r
630         return ENOMEM;\r
631 \r
632     /* dot entry has the smallest size available */\r
633     uint32_t max_entry_count =\r
634             block_size / sizeof(struct ext4_directory_dx_dot_entry);\r
635 \r
636     /* Allocate sort entry */\r
637     struct ext4_dx_sort_entry *sort_array =\r
638             malloc(max_entry_count * sizeof(struct ext4_dx_sort_entry));\r
639 \r
640     if (sort_array == NULL) {\r
641         free(entry_buffer);\r
642         return ENOMEM;\r
643     }\r
644 \r
645     uint32_t idx = 0;\r
646     uint32_t real_size = 0;\r
647 \r
648     /* Initialize hinfo */\r
649     struct ext4_hash_info tmp_hinfo;\r
650     memcpy(&tmp_hinfo, hinfo, sizeof(struct ext4_hash_info));\r
651 \r
652     /* Load all valid entries to the buffer */\r
653     struct ext4_directory_entry_ll *dentry = (void *)old_data_block->data;\r
654     uint8_t *entry_buffer_ptr = entry_buffer;\r
655     while ((void *)dentry < (void *)(old_data_block->data + block_size)) {\r
656         /* Read only valid entries */\r
657         if (ext4_dir_entry_ll_get_inode(dentry) && dentry->name_length) {\r
658             uint8_t len = ext4_dir_entry_ll_get_name_length(\r
659                     &inode_ref->fs->sb, dentry);\r
660 \r
661             rc = ext4_dir_dx_hash_string(&tmp_hinfo, len, (char*)dentry->name);\r
662             if(rc != EOK) {\r
663                 free(sort_array);\r
664                 free(entry_buffer);\r
665                 return rc;\r
666             }\r
667 \r
668             uint32_t rec_len = 8 + len;\r
669 \r
670             if ((rec_len % 4) != 0)\r
671                 rec_len += 4 - (rec_len % 4);\r
672 \r
673             memcpy(entry_buffer_ptr, dentry, rec_len);\r
674 \r
675             sort_array[idx].dentry = entry_buffer_ptr;\r
676             sort_array[idx].rec_len = rec_len;\r
677             sort_array[idx].hash = tmp_hinfo.hash;\r
678 \r
679             entry_buffer_ptr += rec_len;\r
680             real_size += rec_len;\r
681             idx++;\r
682         }\r
683 \r
684         dentry = (void *)((uint8_t *)dentry +\r
685                 ext4_dir_entry_ll_get_entry_length(dentry));\r
686     }\r
687 \r
688     /* Sort all entries */\r
689     qsort(sort_array, idx, sizeof(struct ext4_dx_sort_entry),\r
690         ext4_dir_dx_entry_comparator);\r
691 \r
692     /* Allocate new block for store the second part of entries */\r
693     uint32_t new_fblock;\r
694     uint32_t new_iblock;\r
695     rc = ext4_fs_append_inode_block(inode_ref, &new_fblock,\r
696             &new_iblock);\r
697     if (rc != EOK) {\r
698         free(sort_array);\r
699         free(entry_buffer);\r
700         return rc;\r
701     }\r
702 \r
703     /* Load new block */\r
704     struct ext4_block new_data_block_tmp;\r
705     rc = ext4_block_get(inode_ref->fs->bdev, &new_data_block_tmp,\r
706             new_fblock);\r
707     if (rc != EOK) {\r
708         free(sort_array);\r
709         free(entry_buffer);\r
710         return rc;\r
711     }\r
712 \r
713     /*\r
714      * Distribute entries to two blocks (by size)\r
715      * - compute the half\r
716      */\r
717     uint32_t new_hash = 0;\r
718     uint32_t current_size = 0;\r
719     uint32_t mid = 0;\r
720     uint32_t i;\r
721     for ( i = 0; i < idx; ++i) {\r
722         if ((current_size + sort_array[i].rec_len) > (block_size / 2)) {\r
723             new_hash = sort_array[i].hash;\r
724             mid = i;\r
725             break;\r
726         }\r
727 \r
728         current_size += sort_array[i].rec_len;\r
729     }\r
730 \r
731     /* Check hash collision */\r
732     uint32_t continued = 0;\r
733     if (new_hash == sort_array[mid-1].hash)\r
734         continued = 1;\r
735 \r
736     uint32_t offset = 0;\r
737     void *ptr;\r
738 \r
739     /* First part - to the old block */\r
740     for (i = 0; i < mid; ++i) {\r
741         ptr = old_data_block->data + offset;\r
742         memcpy(ptr, sort_array[i].dentry, sort_array[i].rec_len);\r
743 \r
744         struct ext4_directory_entry_ll *tmp = ptr;\r
745         if (i < (mid - 1))\r
746             ext4_dir_entry_ll_set_entry_length(tmp,\r
747                     sort_array[i].rec_len);\r
748         else\r
749             ext4_dir_entry_ll_set_entry_length(tmp,\r
750                     block_size - offset);\r
751 \r
752         offset += sort_array[i].rec_len;\r
753     }\r
754 \r
755     /* Second part - to the new block */\r
756     offset = 0;\r
757     for (i = mid; i < idx; ++i) {\r
758         ptr = new_data_block_tmp.data + offset;\r
759         memcpy(ptr, sort_array[i].dentry, sort_array[i].rec_len);\r
760 \r
761         struct ext4_directory_entry_ll *tmp = ptr;\r
762         if (i < (idx - 1))\r
763             ext4_dir_entry_ll_set_entry_length(tmp,\r
764                     sort_array[i].rec_len);\r
765         else\r
766             ext4_dir_entry_ll_set_entry_length(tmp,\r
767                     block_size - offset);\r
768 \r
769         offset += sort_array[i].rec_len;\r
770     }\r
771 \r
772     /* Do some steps to finish operation */\r
773     old_data_block->dirty = true;\r
774     new_data_block_tmp.dirty = true;\r
775 \r
776     free(sort_array);\r
777     free(entry_buffer);\r
778 \r
779     ext4_dir_dx_insert_entry(index_block, new_hash + continued,\r
780         new_iblock);\r
781 \r
782     *new_data_block = new_data_block_tmp;\r
783 \r
784     return EOK;\r
785 }\r
786 \r
787 /**@brief  Split index node and maybe some parent nodes in the tree hierarchy.\r
788  * @param inode_ref Directory i-node\r
789  * @param dx_blocks Array with path from root to leaf node\r
790  * @param dx_block  Leaf block to be split if needed\r
791  * @return Error code\r
792  */\r
793 static int ext4_dir_dx_split_index(struct ext4_inode_ref *inode_ref,\r
794     struct  ext4_directory_dx_block *dx_blocks,\r
795     struct ext4_directory_dx_block *dx_block,\r
796     struct ext4_directory_dx_block **new_dx_block)\r
797 {\r
798     struct ext4_directory_dx_entry *entries;\r
799 \r
800     if (dx_block == dx_blocks)\r
801         entries =\r
802                 ((struct  ext4_directory_dx_root *) dx_block->block.data)->entries;\r
803     else\r
804         entries =\r
805                 ((struct ext4_directory_dx_node *) dx_block->block.data)->entries;\r
806 \r
807     struct ext4_directory_dx_countlimit *countlimit =\r
808             (struct ext4_directory_dx_countlimit *) entries;\r
809 \r
810     uint16_t leaf_limit =\r
811             ext4_dir_dx_countlimit_get_limit(countlimit);\r
812     uint16_t leaf_count =\r
813             ext4_dir_dx_countlimit_get_count(countlimit);\r
814 \r
815     /* Check if is necessary to split index block */\r
816     if (leaf_limit == leaf_count) {\r
817         size_t levels = dx_block - dx_blocks;\r
818 \r
819         struct ext4_directory_dx_entry *root_entries =\r
820                 ((struct ext4_directory_dx_root *) dx_blocks[0].block.data)->entries;\r
821 \r
822         struct ext4_directory_dx_countlimit *root_countlimit =\r
823                 (struct ext4_directory_dx_countlimit *) root_entries;\r
824         uint16_t root_limit =\r
825                 ext4_dir_dx_countlimit_get_limit(root_countlimit);\r
826         uint16_t root_count =\r
827                 ext4_dir_dx_countlimit_get_count(root_countlimit);\r
828 \r
829         /* Linux limitation */\r
830         if ((levels > 0) && (root_limit == root_count))\r
831             return ENOSPC;\r
832 \r
833         /* Add new block to directory */\r
834         uint32_t new_fblock;\r
835         uint32_t new_iblock;\r
836         int rc = ext4_fs_append_inode_block(inode_ref,\r
837                 &new_fblock, &new_iblock);\r
838         if (rc != EOK)\r
839             return rc;\r
840 \r
841         /* load new block */\r
842         struct ext4_block new_block;\r
843         rc = ext4_block_get(inode_ref->fs->bdev, &new_block,\r
844                 new_fblock);\r
845         if (rc != EOK)\r
846             return rc;\r
847 \r
848         struct ext4_directory_dx_node  *new_node = (void *)new_block.data;\r
849         struct ext4_directory_dx_entry *new_entries = new_node->entries;\r
850 \r
851         memset(&new_node->fake, 0, sizeof(struct ext4_fake_directory_entry));\r
852 \r
853         uint32_t block_size =\r
854                 ext4_sb_get_block_size(&inode_ref->fs->sb);\r
855 \r
856         new_node->fake.entry_length = block_size;\r
857 \r
858         /* Split leaf node */\r
859         if (levels > 0) {\r
860             uint32_t count_left = leaf_count / 2;\r
861             uint32_t count_right = leaf_count - count_left;\r
862             uint32_t hash_right =\r
863                     ext4_dir_dx_entry_get_hash(entries + count_left);\r
864 \r
865             /* Copy data to new node */\r
866             memcpy((void *) new_entries, (void *) (entries + count_left),\r
867                 count_right * sizeof(struct ext4_directory_dx_entry));\r
868 \r
869             /* Initialize new node */\r
870             struct ext4_directory_dx_countlimit *left_countlimit =\r
871                     (struct ext4_directory_dx_countlimit *) entries;\r
872             struct ext4_directory_dx_countlimit *right_countlimit =\r
873                     (struct ext4_directory_dx_countlimit *) new_entries;\r
874 \r
875             ext4_dir_dx_countlimit_set_count(left_countlimit, count_left);\r
876             ext4_dir_dx_countlimit_set_count(right_countlimit, count_right);\r
877 \r
878             uint32_t entry_space =\r
879                     block_size - sizeof(struct ext4_fake_directory_entry);\r
880             uint32_t node_limit =\r
881                     entry_space / sizeof(struct ext4_directory_dx_entry);\r
882             ext4_dir_dx_countlimit_set_limit(right_countlimit, node_limit);\r
883 \r
884             /* Which index block is target for new entry */\r
885             uint32_t position_index = (dx_block->position - dx_block->entries);\r
886             if (position_index >= count_left) {\r
887                 dx_block->block.dirty = true;\r
888 \r
889                 struct ext4_block block_tmp = dx_block->block;\r
890 \r
891 \r
892                 dx_block->block = new_block;\r
893 \r
894                 dx_block->position =\r
895                         new_entries + position_index - count_left;\r
896                 dx_block->entries = new_entries;\r
897 \r
898                 new_block = block_tmp;\r
899             }\r
900 \r
901             /* Finally insert new entry */\r
902             ext4_dir_dx_insert_entry(dx_blocks, hash_right, new_iblock);\r
903             dx_blocks[0].block.dirty = true;\r
904             dx_blocks[1].block.dirty = true;\r
905 \r
906             new_block.dirty = true;\r
907             return ext4_block_set(inode_ref->fs->bdev, &new_block);\r
908         } else {\r
909             /* Create second level index */\r
910 \r
911             /* Copy data from root to child block */\r
912             memcpy((void *) new_entries, (void *) entries,\r
913                     leaf_count * sizeof(struct ext4_directory_dx_entry));\r
914 \r
915             struct ext4_directory_dx_countlimit *new_countlimit =\r
916                     (struct ext4_directory_dx_countlimit *) new_entries;\r
917 \r
918             uint32_t entry_space =\r
919                     block_size - sizeof(struct ext4_fake_directory_entry);\r
920             uint32_t node_limit =\r
921                     entry_space / sizeof(struct ext4_directory_dx_entry);\r
922             ext4_dir_dx_countlimit_set_limit(new_countlimit, node_limit);\r
923 \r
924             /* Set values in root node */\r
925             struct ext4_directory_dx_countlimit *new_root_countlimit =\r
926                     (struct ext4_directory_dx_countlimit *) entries;\r
927 \r
928             ext4_dir_dx_countlimit_set_count(new_root_countlimit, 1);\r
929             ext4_dir_dx_entry_set_block(entries, new_iblock);\r
930 \r
931             ((struct ext4_directory_dx_root *)\r
932                     dx_blocks[0].block.data)->info.indirect_levels = 1;\r
933 \r
934             /* Add new entry to the path */\r
935             dx_block = dx_blocks + 1;\r
936             dx_block->position = dx_blocks->position - entries + new_entries;\r
937             dx_block->entries = new_entries;\r
938             dx_block->block = new_block;\r
939 \r
940             *new_dx_block = dx_block;\r
941 \r
942             dx_blocks[0].block.dirty = true;\r
943             dx_blocks[1].block.dirty = true;\r
944         }\r
945     }\r
946 \r
947     return EOK;\r
948 }\r
949 \r
950 int ext4_dir_dx_add_entry(struct ext4_inode_ref *parent,\r
951     struct ext4_inode_ref *child, const char *name)\r
952 {\r
953     int rc2 = EOK;\r
954 \r
955     /* Get direct block 0 (index root) */\r
956     uint32_t root_block_addr;\r
957     int rc = ext4_fs_get_inode_data_block_index(parent, 0,\r
958             &root_block_addr);\r
959     if (rc != EOK)\r
960         return rc;\r
961 \r
962     struct ext4_fs *fs = parent->fs;\r
963     struct ext4_block root_block;\r
964 \r
965     rc = ext4_block_get(fs->bdev, &root_block, root_block_addr);\r
966     if (rc != EOK)\r
967         return rc;\r
968 \r
969     /* Initialize hinfo structure (mainly compute hash) */\r
970     uint32_t name_len = strlen(name);\r
971     struct ext4_hash_info hinfo;\r
972     rc = ext4_dir_hinfo_init(&hinfo, &root_block, &fs->sb,\r
973             name_len, name);\r
974     if (rc != EOK) {\r
975         ext4_block_set(fs->bdev, &root_block);\r
976         return EXT4_ERR_BAD_DX_DIR;\r
977     }\r
978 \r
979     /*\r
980      * Hardcoded number 2 means maximum height of index\r
981      * tree defined in Linux.\r
982      */\r
983     struct ext4_directory_dx_block dx_blocks[2];\r
984     struct ext4_directory_dx_block *dx_block;\r
985     struct ext4_directory_dx_block *dx_it;\r
986 \r
987     rc = ext4_dir_dx_get_leaf(&hinfo, parent, &root_block,\r
988             &dx_block, dx_blocks);\r
989     if (rc != EOK) {\r
990         rc = EXT4_ERR_BAD_DX_DIR;\r
991         goto release_index;\r
992     }\r
993 \r
994     /* Try to insert to existing data block */\r
995     uint32_t leaf_block_idx =\r
996             ext4_dir_dx_entry_get_block(dx_block->position);\r
997     uint32_t leaf_block_addr;\r
998     rc = ext4_fs_get_inode_data_block_index(parent, leaf_block_idx,\r
999             &leaf_block_addr);\r
1000     if (rc != EOK)\r
1001         goto release_index;\r
1002 \r
1003     /*\r
1004      * Check if there is needed to split index node\r
1005      * (and recursively also parent nodes)\r
1006      */\r
1007     rc = ext4_dir_dx_split_index(parent, dx_blocks, dx_block, &dx_block);\r
1008     if (rc != EOK)\r
1009         goto release_target_index;\r
1010 \r
1011     struct ext4_block target_block;\r
1012     rc = ext4_block_get(fs->bdev, &target_block, leaf_block_addr);\r
1013     if (rc != EOK)\r
1014         goto release_index;\r
1015 \r
1016 \r
1017     /* Check if insert operation passed */\r
1018     rc = ext4_dir_try_insert_entry(&fs->sb, &target_block, child,\r
1019             name, name_len);\r
1020     if (rc == EOK)\r
1021         goto release_target_index;\r
1022 \r
1023 \r
1024     /* Split entries to two blocks (includes sorting by hash value) */\r
1025     struct ext4_block new_block;\r
1026     rc = ext4_dir_dx_split_data(parent, &hinfo, &target_block,\r
1027             dx_block, &new_block);\r
1028     if (rc != EOK) {\r
1029         rc2 = rc;\r
1030         goto release_target_index;\r
1031     }\r
1032 \r
1033     /* Where to save new entry */\r
1034     uint32_t new_block_hash =\r
1035             ext4_dir_dx_entry_get_hash(dx_block->position + 1);\r
1036     if (hinfo.hash >= new_block_hash)\r
1037         rc = ext4_dir_try_insert_entry(&fs->sb, &new_block,\r
1038                 child, name, name_len);\r
1039     else\r
1040         rc = ext4_dir_try_insert_entry(&fs->sb, &target_block,\r
1041                 child, name, name_len);\r
1042 \r
1043     /* Cleanup */\r
1044     rc = ext4_block_set(fs->bdev, &new_block);\r
1045     if (rc != EOK)\r
1046         return rc;\r
1047 \r
1048     /* Cleanup operations */\r
1049 \r
1050     release_target_index:\r
1051     rc2 = rc;\r
1052 \r
1053     rc = ext4_block_set(fs->bdev, &target_block);\r
1054     if (rc != EOK)\r
1055         return rc;\r
1056 \r
1057     release_index:\r
1058     if (rc != EOK)\r
1059         rc2 = rc;\r
1060 \r
1061     dx_it = dx_blocks;\r
1062 \r
1063     while (dx_it <= dx_block) {\r
1064         rc = ext4_block_set(fs->bdev, &dx_it->block);\r
1065         if (rc != EOK)\r
1066             return rc;\r
1067 \r
1068         dx_it++;\r
1069     }\r
1070 \r
1071     return rc2;\r
1072 }\r
1073 \r
1074 /**\r
1075  * @}\r
1076  */\r
1077 \r