97878cae49442abad2df5ebffcf90cc0703c046e
[lwext4.git] / lwext4 / ext4_dir_idx.c
1 /*
2  * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * - Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  * - Redistributions in binary form must reproduce the above copyright
12  *   notice, this list of conditions and the following disclaimer in the
13  *   documentation and/or other materials provided with the distribution.
14  * - The name of the author may not be used to endorse or promote products
15  *   derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 /** @addtogroup lwext4
30  * @{
31  */
32 /**
33  * @file  ext4_dir_idx.c
34  * @brief Directory indexing procedures.
35  */
36
37 #include "ext4_config.h"
38 #include "ext4_dir_idx.h"
39 #include "ext4_dir.h"
40 #include "ext4_blockdev.h"
41 #include "ext4_fs.h"
42 #include "ext4_super.h"
43 #include "ext4_crc32c.h"
44 #include "ext4_hash.h"
45
46 #include <string.h>
47 #include <stdlib.h>
48
49 /**@brief Get hash version used in directory index.
50  * @param root_info Pointer to root info structure of index
51  * @return Hash algorithm version
52  */
53 static inline uint8_t ext4_dir_dx_root_info_get_hash_version(
54     struct ext4_directory_dx_root_info *root_info)
55 {
56         return root_info->hash_version;
57 }
58
59 /**@brief Set hash version, that will be used in directory index.
60  * @param root_info Pointer to root info structure of index
61  * @param v Hash algorithm version
62  */
63 static inline void ext4_dir_dx_root_info_set_hash_version(
64     struct ext4_directory_dx_root_info *root_info, uint8_t v)
65 {
66         root_info->hash_version = v;
67 }
68
69 /**@brief Get length of root_info structure in bytes.
70  * @param root_info Pointer to root info structure of index
71  * @return Length of the structure
72  */
73 static inline uint8_t ext4_dir_dx_root_info_get_info_length(
74     struct ext4_directory_dx_root_info *root_info)
75 {
76         return root_info->info_length;
77 }
78
79 /**@brief Set length of root_info structure in bytes.
80  * @param root_info   Pointer to root info structure of index
81  * @param info_length Length of the structure
82  */
83 static inline void ext4_dir_dx_root_info_set_info_length(
84     struct ext4_directory_dx_root_info *root_info, uint8_t len)
85 {
86         root_info->info_length = len;
87 }
88
89 /**@brief Get number of indirect levels of HTree.
90  * @param root_info Pointer to root info structure of index
91  * @return Height of HTree (actually only 0 or 1)
92  */
93 static inline uint8_t ext4_dir_dx_root_info_get_indirect_levels(
94     struct ext4_directory_dx_root_info *root_info)
95 {
96         return root_info->indirect_levels;
97 }
98
99 /**@brief Set number of indirect levels of HTree.
100  * @param root_info Pointer to root info structure of index
101  * @param lvl Height of HTree (actually only 0 or 1)
102  */
103 static inline void ext4_dir_dx_root_info_set_indirect_levels(
104     struct ext4_directory_dx_root_info *root_info, uint8_t lvl)
105 {
106         root_info->indirect_levels = lvl;
107 }
108
109 /**@brief Get maximum number of index node entries.
110  * @param climit Pointer to counlimit structure
111  * @return Maximum of entries in node
112  */
113 static inline uint16_t
114 ext4_dir_dx_countlimit_get_limit(struct ext4_directory_dx_countlimit *climit)
115 {
116         return to_le16(climit->limit);
117 }
118
119 /**@brief Set maximum number of index node entries.
120  * @param climit Pointer to counlimit structure
121  * @param limit Maximum of entries in node
122  */
123 static inline void
124 ext4_dir_dx_countlimit_set_limit(struct ext4_directory_dx_countlimit *climit,
125                                  uint16_t limit)
126 {
127         climit->limit = to_le16(limit);
128 }
129
130 /**@brief Get current number of index node entries.
131  * @param climit Pointer to counlimit structure
132  * @return Number of entries in node
133  */
134 static inline uint16_t
135 ext4_dir_dx_countlimit_get_count(struct ext4_directory_dx_countlimit *climit)
136 {
137         return to_le16(climit->count);
138 }
139
140 /**@brief Set current number of index node entries.
141  * @param climit Pointer to counlimit structure
142  * @param count Number of entries in node
143  */
144 static inline void
145 ext4_dir_dx_countlimit_set_count(struct ext4_directory_dx_countlimit *climit,
146                                  uint16_t count)
147 {
148         climit->count = to_le16(count);
149 }
150
151 /**@brief Get hash value of index entry.
152  * @param entry Pointer to index entry
153  * @return Hash value
154  */
155 static inline uint32_t
156 ext4_dir_dx_entry_get_hash(struct ext4_directory_dx_entry *entry)
157 {
158         return to_le32(entry->hash);
159 }
160
161 /**@brief Set hash value of index entry.
162  * @param entry Pointer to index entry
163  * @param hash  Hash value
164  */
165 static inline void
166 ext4_dir_dx_entry_set_hash(struct ext4_directory_dx_entry *entry, uint32_t hash)
167 {
168         entry->hash = to_le32(hash);
169 }
170
171 /**@brief Get block address where child node is located.
172  * @param entry Pointer to index entry
173  * @return Block address of child node
174  */
175 static inline uint32_t
176 ext4_dir_dx_entry_get_block(struct ext4_directory_dx_entry *entry)
177 {
178         return to_le32(entry->block);
179 }
180
181 /**@brief Set block address where child node is located.
182  * @param entry Pointer to index entry
183  * @param block Block address of child node
184  */
185 static inline void
186 ext4_dir_dx_entry_set_block(struct ext4_directory_dx_entry *entry,
187                             uint32_t block)
188 {
189         entry->block = to_le32(block);
190 }
191
192 /**@brief Sort entry item.*/
193 struct ext4_dx_sort_entry {
194         uint32_t hash;
195         uint32_t rec_len;
196         void *dentry;
197 };
198
199 static int ext4_dir_dx_hash_string(struct ext4_hash_info *hinfo, int len,
200                                    const char *name)
201 {
202         return ext2_htree_hash(name, len, hinfo->seed, hinfo->hash_version,
203                                &hinfo->hash, &hinfo->minor_hash);
204 }
205
206 static uint32_t
207 ext4_dir_dx_checksum(struct ext4_inode_ref *inode_ref,
208                  void *dirent,
209                  int count_offset, int count, struct ext4_directory_dx_tail *t)
210 {
211         uint32_t orig_checksum, checksum = 0;
212         struct ext4_sblock *sb = &inode_ref->fs->sb;
213         int size;
214
215         /* Compute the checksum only if the filesystem supports it */
216         if (ext4_sb_has_feature_read_only(sb,
217                                 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
218                 size = count_offset +
219                         (count * sizeof(struct ext4_directory_dx_tail));
220                 orig_checksum = t->checksum;
221                 t->checksum = 0;
222                 /* First calculate crc32 checksum against fs uuid */
223                 checksum = ext4_crc32c(~0, sb->uuid, sizeof(sb->uuid));
224                 /* Then calculate crc32 checksum against all the dx_entry */
225                 checksum = ext4_crc32c(checksum, dirent, size);
226                 /* Finally calculate crc32 checksum for dx_tail */
227                 checksum = ext4_crc32c(checksum, t,
228                                 sizeof(struct ext4_directory_dx_tail));
229                 t->checksum = orig_checksum;
230         }
231         return checksum;
232 }
233
234 static struct ext4_directory_dx_countlimit *
235 ext4_dir_dx_get_countlimit(struct ext4_inode_ref *inode_ref,
236                         struct ext4_directory_entry_ll *dirent,
237                         int *offset)
238 {
239         struct ext4_directory_entry_ll *dp;
240         struct ext4_directory_dx_root *root;
241         struct ext4_sblock *sb = &inode_ref->fs->sb;
242         int count_offset;
243
244         if (ext4_dir_entry_ll_get_entry_length(dirent) ==
245                         ext4_sb_get_block_size(sb))
246                 count_offset = 8;
247         else if (ext4_dir_entry_ll_get_entry_length(dirent) == 12) {
248                 root = (struct ext4_directory_dx_root *)dirent;
249                 dp = (struct ext4_directory_entry_ll *)&root->dots[1];
250                 if (ext4_dir_entry_ll_get_entry_length(dp) !=
251                     ext4_sb_get_block_size(sb) - 12)
252                         return NULL;
253                 if (root->info.reserved_zero ||
254                     root->info.info_length != sizeof(struct ext4_directory_dx_root_info))
255                         return NULL;
256                 count_offset = 32;
257         } else
258                 return NULL;
259
260         if (offset)
261                 *offset = count_offset;
262         return (struct ext4_directory_dx_countlimit *)(((char *)dirent) + count_offset);
263 }
264
265 /*
266  * BIG FAT NOTES:
267  *       Currently we do not verify the checksum of HTree node.
268  */
269 __unused static int
270 ext4_dir_dx_checksum_verify(struct ext4_inode_ref *inode_ref,
271                                 struct ext4_directory_entry_ll *dirent)
272 {
273         struct ext4_sblock *sb = &inode_ref->fs->sb;
274         int count_offset, limit, count;
275
276         if (ext4_sb_has_feature_read_only(sb,
277                                 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
278                 struct ext4_directory_dx_countlimit *countlimit =
279                         ext4_dir_dx_get_countlimit(inode_ref, dirent, &count_offset);
280                 if (!countlimit) {
281                         /* Directory seems corrupted. */
282                         return 1;
283                 }
284                 struct ext4_directory_dx_tail *t;
285                 limit = ext4_dir_dx_countlimit_get_limit(countlimit);
286                 count = ext4_dir_dx_countlimit_get_count(countlimit);
287                 if (count_offset + (limit * sizeof(struct ext4_directory_dx_entry)) >
288                                 ext4_sb_get_block_size(sb) -
289                                 sizeof(struct ext4_directory_dx_tail)) {
290                         /* There is no space to hold the checksum */
291                         return 1;
292                 }
293                 t = (struct ext4_directory_dx_tail *)
294                         (((struct ext4_directory_dx_entry *)countlimit) + limit);
295
296                 if (t->checksum != to_le32(ext4_dir_dx_checksum(inode_ref,
297                                                                 dirent,
298                                                                 count_offset,
299                                                                 count, t)))
300                         return 0;
301         }
302         return 1;
303 }
304
305 static void
306 ext4_dir_set_dx_checksum(struct ext4_inode_ref *inode_ref,
307                         struct ext4_directory_entry_ll *dirent)
308 {
309         int count_offset, limit, count;
310         struct ext4_sblock *sb = &inode_ref->fs->sb;
311
312         if (ext4_sb_has_feature_read_only(sb,
313                                 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
314                 struct ext4_directory_dx_countlimit *countlimit =
315                         ext4_dir_dx_get_countlimit(inode_ref, dirent, &count_offset);
316                 if (!countlimit) {
317                         /* Directory seems corrupted. */
318                         return;
319                 }
320                 struct ext4_directory_dx_tail *t;
321                 limit = ext4_dir_dx_countlimit_get_limit(countlimit);
322                 count = ext4_dir_dx_countlimit_get_count(countlimit);
323                 if (count_offset + (limit * sizeof(struct ext4_directory_dx_entry)) >
324                                 ext4_sb_get_block_size(sb) -
325                                 sizeof(struct ext4_directory_dx_tail)) {
326                         /* There is no space to hold the checksum */
327                         return;
328                 }
329                 t = (struct ext4_directory_dx_tail *)
330                         (((struct ext4_directory_dx_entry *)countlimit) + limit);
331
332                 t->checksum =
333                         to_le32(ext4_dir_dx_checksum(inode_ref, dirent, count_offset, count, t));
334         }
335 }
336
337 /****************************************************************************/
338
339 int ext4_dir_dx_init(struct ext4_inode_ref *dir)
340 {
341         /* Load block 0, where will be index root located */
342         ext4_fsblk_t fblock;
343         struct ext4_sblock *sb = &dir->fs->sb;
344         int rc = ext4_fs_get_inode_data_block_index(dir, 0, &fblock, false);
345         if (rc != EOK)
346                 return rc;
347
348         struct ext4_block block;
349         rc = ext4_block_get(dir->fs->bdev, &block, fblock);
350         if (rc != EOK)
351                 return rc;
352
353         /* Initialize pointers to data structures */
354         struct ext4_directory_dx_root *root = (void *)block.data;
355         struct ext4_directory_dx_root_info *info = &(root->info);
356
357         /* Initialize root info structure */
358         uint8_t hash_version = ext4_get8(&dir->fs->sb, default_hash_version);
359
360         ext4_dir_dx_root_info_set_hash_version(info, hash_version);
361         ext4_dir_dx_root_info_set_indirect_levels(info, 0);
362         ext4_dir_dx_root_info_set_info_length(info, 8);
363
364         /* Set limit and current number of entries */
365         struct ext4_directory_dx_countlimit *countlimit =
366             (struct ext4_directory_dx_countlimit *)&root->entries;
367
368         ext4_dir_dx_countlimit_set_count(countlimit, 1);
369
370         uint32_t block_size = ext4_sb_get_block_size(&dir->fs->sb);
371         uint32_t entry_space = block_size -
372                                2 * sizeof(struct ext4_directory_dx_dot_entry) -
373                                sizeof(struct ext4_directory_dx_root_info);
374         uint16_t root_limit =
375             entry_space / sizeof(struct ext4_directory_dx_entry);
376         ext4_dir_dx_countlimit_set_limit(countlimit, root_limit);
377
378         /* Append new block, where will be new entries inserted in the future */
379         uint32_t iblock;
380         rc = ext4_fs_append_inode_block(dir, &fblock, &iblock);
381         if (rc != EOK) {
382                 ext4_block_set(dir->fs->bdev, &block);
383                 return rc;
384         }
385
386         struct ext4_block new_block;
387
388         rc = ext4_block_get(dir->fs->bdev, &new_block, fblock);
389         if (rc != EOK) {
390                 ext4_block_set(dir->fs->bdev, &block);
391                 return rc;
392         }
393
394         /* Fill the whole block with empty entry */
395         struct ext4_directory_entry_ll *block_entry = (void *)new_block.data;
396
397         if (ext4_sb_has_feature_read_only(sb,
398                                           EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
399                 ext4_dir_entry_ll_set_entry_length(block_entry,
400                                 sizeof(struct ext4_directory_entry_tail));
401                 ext4_dir_entry_ll_set_name_length(sb,
402                                                   block_entry,
403                                                   0);
404                 ext4_dir_entry_ll_set_inode_type(sb,
405                                                  block_entry,
406                                                  EXT4_DIRENTRY_DIR_CSUM);
407                 ext4_dir_set_checksum(dir,
408                                 (struct ext4_directory_entry_ll *)new_block.data);
409         } else {
410                 ext4_dir_entry_ll_set_entry_length(block_entry, block_size);
411         }
412
413         ext4_dir_entry_ll_set_inode(block_entry, 0);
414
415         new_block.dirty = true;
416         rc = ext4_block_set(dir->fs->bdev, &new_block);
417         if (rc != EOK) {
418                 ext4_block_set(dir->fs->bdev, &block);
419                 return rc;
420         }
421
422         /* Connect new block to the only entry in index */
423         struct ext4_directory_dx_entry *entry = root->entries;
424         ext4_dir_dx_entry_set_block(entry, iblock);
425
426         ext4_dir_set_dx_checksum(dir,
427                         (struct ext4_directory_entry_ll *)block.data);
428         block.dirty = true;
429
430         return ext4_block_set(dir->fs->bdev, &block);
431 }
432
433 /**@brief Initialize hash info structure necessary for index operations.
434  * @param hinfo      Pointer to hinfo to be initialized
435  * @param root_block Root block (number 0) of index
436  * @param sb         Pointer to superblock
437  * @param name_len   Length of name to be computed hash value from
438  * @param name       Name to be computed hash value from
439  * @return Standard error code
440  */
441 static int ext4_dir_hinfo_init(struct ext4_hash_info *hinfo,
442                                struct ext4_block *root_block,
443                                struct ext4_sblock *sb, size_t name_len,
444                                const char *name)
445 {
446         struct ext4_directory_dx_root *root =
447             (struct ext4_directory_dx_root *)root_block->data;
448
449         if ((root->info.hash_version != EXT2_HTREE_LEGACY) &&
450             (root->info.hash_version != EXT2_HTREE_HALF_MD4) &&
451             (root->info.hash_version != EXT2_HTREE_TEA))
452                 return EXT4_ERR_BAD_DX_DIR;
453
454         /* Check unused flags */
455         if (root->info.unused_flags != 0)
456                 return EXT4_ERR_BAD_DX_DIR;
457
458         /* Check indirect levels */
459         if (root->info.indirect_levels > 1)
460                 return EXT4_ERR_BAD_DX_DIR;
461
462         /* Check if node limit is correct */
463         uint32_t block_size = ext4_sb_get_block_size(sb);
464         uint32_t entry_space = block_size;
465         entry_space -= 2 * sizeof(struct ext4_directory_dx_dot_entry);
466         entry_space -= sizeof(struct ext4_directory_dx_root_info);
467         entry_space = entry_space / sizeof(struct ext4_directory_dx_entry);
468
469         uint16_t limit = ext4_dir_dx_countlimit_get_limit(
470             (struct ext4_directory_dx_countlimit *)&root->entries);
471         if (limit != entry_space)
472                 return EXT4_ERR_BAD_DX_DIR;
473
474         /* Check hash version and modify if necessary */
475         hinfo->hash_version =
476             ext4_dir_dx_root_info_get_hash_version(&root->info);
477         if ((hinfo->hash_version <= EXT2_HTREE_TEA) &&
478             (ext4_sb_check_flag(sb, EXT4_SUPERBLOCK_FLAGS_UNSIGNED_HASH))) {
479                 /* Use unsigned hash */
480                 hinfo->hash_version += 3;
481         }
482
483         /* Load hash seed from superblock */
484
485         hinfo->seed = ext4_get8(sb, hash_seed);
486
487         /* Compute hash value of name */
488         if (name)
489                 return ext4_dir_dx_hash_string(hinfo, name_len, name);
490
491         return EOK;
492 }
493
494 /**@brief Walk through index tree and load leaf with corresponding hash value.
495  * @param hinfo      Initialized hash info structure
496  * @param inode_ref  Current i-node
497  * @param root_block Root block (iblock 0), where is root node located
498  * @param dx_block   Pointer to leaf node in dx_blocks array
499  * @param dx_blocks  Array with the whole path from root to leaf
500  * @return Standard error code
501  */
502 static int ext4_dir_dx_get_leaf(struct ext4_hash_info *hinfo,
503                                 struct ext4_inode_ref *inode_ref,
504                                 struct ext4_block *root_block,
505                                 struct ext4_directory_dx_block **dx_block,
506                                 struct ext4_directory_dx_block *dx_blocks)
507 {
508         struct ext4_directory_dx_block *tmp_dx_block = dx_blocks;
509         struct ext4_directory_dx_root *root =
510             (struct ext4_directory_dx_root *)root_block->data;
511         struct ext4_directory_dx_entry *entries =
512             (struct ext4_directory_dx_entry *)&root->entries;
513
514         uint16_t limit = ext4_dir_dx_countlimit_get_limit(
515             (struct ext4_directory_dx_countlimit *)entries);
516         uint8_t indirect_level =
517             ext4_dir_dx_root_info_get_indirect_levels(&root->info);
518
519         struct ext4_block *tmp_block = root_block;
520         struct ext4_directory_dx_entry *p;
521         struct ext4_directory_dx_entry *q;
522         struct ext4_directory_dx_entry *m;
523         struct ext4_directory_dx_entry *at;
524
525         /* Walk through the index tree */
526         while (true) {
527                 uint16_t count = ext4_dir_dx_countlimit_get_count(
528                     (struct ext4_directory_dx_countlimit *)entries);
529                 if ((count == 0) || (count > limit))
530                         return EXT4_ERR_BAD_DX_DIR;
531
532                 /* Do binary search in every node */
533                 p = entries + 1;
534                 q = entries + count - 1;
535
536                 while (p <= q) {
537                         m = p + (q - p) / 2;
538                         if (ext4_dir_dx_entry_get_hash(m) > hinfo->hash)
539                                 q = m - 1;
540                         else
541                                 p = m + 1;
542                 }
543
544                 at = p - 1;
545
546                 /* Write results */
547
548                 memcpy(&tmp_dx_block->block, tmp_block,
549                        sizeof(struct ext4_block));
550                 tmp_dx_block->entries = entries;
551                 tmp_dx_block->position = at;
552
553                 /* Is algorithm in the leaf? */
554                 if (indirect_level == 0) {
555                         *dx_block = tmp_dx_block;
556                         return EOK;
557                 }
558
559                 /* Goto child node */
560                 uint32_t next_block = ext4_dir_dx_entry_get_block(at);
561
562                 indirect_level--;
563
564                 ext4_fsblk_t fblock;
565                 int rc = ext4_fs_get_inode_data_block_index(
566                     inode_ref, next_block, &fblock, false);
567                 if (rc != EOK)
568                         return rc;
569
570                 rc = ext4_block_get(inode_ref->fs->bdev, tmp_block, fblock);
571                 if (rc != EOK)
572                         return rc;
573
574                 entries =
575                     ((struct ext4_directory_dx_node *)tmp_block->data)->entries;
576                 limit = ext4_dir_dx_countlimit_get_limit(
577                     (struct ext4_directory_dx_countlimit *)entries);
578
579                 uint16_t entry_space =
580                     ext4_sb_get_block_size(&inode_ref->fs->sb) -
581                     sizeof(struct ext4_fake_directory_entry);
582
583                 entry_space =
584                     entry_space / sizeof(struct ext4_directory_dx_entry);
585
586                 if (limit != entry_space) {
587                         ext4_block_set(inode_ref->fs->bdev, tmp_block);
588                         return EXT4_ERR_BAD_DX_DIR;
589                 }
590
591                 ++tmp_dx_block;
592         }
593
594         /* Unreachable */
595         return EOK;
596 }
597
598 /**@brief Check if the the next block would be checked during entry search.
599  * @param inode_ref Directory i-node
600  * @param hash      Hash value to check
601  * @param dx_block  Current block
602  * @param dx_blocks Array with path from root to leaf node
603  * @return Standard Error code
604  */
605 static int ext4_dir_dx_next_block(struct ext4_inode_ref *inode_ref,
606                                   uint32_t hash,
607                                   struct ext4_directory_dx_block *dx_block,
608                                   struct ext4_directory_dx_block *dx_blocks)
609 {
610         uint32_t num_handles = 0;
611         struct ext4_directory_dx_block *p = dx_block;
612
613         /* Try to find data block with next bunch of entries */
614         while (true) {
615                 p->position++;
616                 uint16_t count = ext4_dir_dx_countlimit_get_count(
617                     (struct ext4_directory_dx_countlimit *)p->entries);
618
619                 if (p->position < p->entries + count)
620                         break;
621
622                 if (p == dx_blocks)
623                         return EOK;
624
625                 num_handles++;
626                 p--;
627         }
628
629         /* Check hash collision (if not occurred - no next block cannot be
630          * used)*/
631         uint32_t current_hash = ext4_dir_dx_entry_get_hash(p->position);
632         if ((hash & 1) == 0) {
633                 if ((current_hash & ~1) != hash)
634                         return 0;
635         }
636
637         /* Fill new path */
638         while (num_handles--) {
639                 uint32_t block_idx = ext4_dir_dx_entry_get_block(p->position);
640                 ext4_fsblk_t block_addr;
641
642                 int rc = ext4_fs_get_inode_data_block_index(
643                     inode_ref, block_idx, &block_addr, false);
644                 if (rc != EOK)
645                         return rc;
646
647                 struct ext4_block block;
648                 rc = ext4_block_get(inode_ref->fs->bdev, &block, block_addr);
649                 if (rc != EOK)
650                         return rc;
651
652                 p++;
653
654                 /* Don't forget to put old block (prevent memory leak) */
655                 rc = ext4_block_set(inode_ref->fs->bdev, &p->block);
656                 if (rc != EOK)
657                         return rc;
658
659                 memcpy(&p->block, &block, sizeof(block));
660                 p->entries =
661                     ((struct ext4_directory_dx_node *)block.data)->entries;
662                 p->position = p->entries;
663         }
664
665         return ENOENT;
666 }
667
668 int ext4_dir_dx_find_entry(struct ext4_directory_search_result *result,
669                            struct ext4_inode_ref *inode_ref, size_t name_len,
670                            const char *name)
671 {
672         /* Load direct block 0 (index root) */
673         ext4_fsblk_t root_block_addr;
674         int rc2;
675         int rc =
676             ext4_fs_get_inode_data_block_index(inode_ref,
677                             0, &root_block_addr, false);
678         if (rc != EOK)
679                 return rc;
680
681         struct ext4_fs *fs = inode_ref->fs;
682
683         struct ext4_block root_block;
684         rc = ext4_block_get(fs->bdev, &root_block, root_block_addr);
685         if (rc != EOK)
686                 return rc;
687
688         /* Initialize hash info (compute hash value) */
689         struct ext4_hash_info hinfo;
690         rc = ext4_dir_hinfo_init(&hinfo, &root_block, &fs->sb, name_len, name);
691         if (rc != EOK) {
692                 ext4_block_set(fs->bdev, &root_block);
693                 return EXT4_ERR_BAD_DX_DIR;
694         }
695
696         /*
697          * Hardcoded number 2 means maximum height of index tree,
698          * specified in the Linux driver.
699          */
700         struct ext4_directory_dx_block dx_blocks[2];
701         struct ext4_directory_dx_block *dx_block;
702         struct ext4_directory_dx_block *tmp;
703
704         rc = ext4_dir_dx_get_leaf(&hinfo, inode_ref, &root_block, &dx_block,
705                                   dx_blocks);
706         if (rc != EOK) {
707                 ext4_block_set(fs->bdev, &root_block);
708                 return EXT4_ERR_BAD_DX_DIR;
709         }
710
711         do {
712                 /* Load leaf block */
713                 uint32_t leaf_block_idx =
714                     ext4_dir_dx_entry_get_block(dx_block->position);
715                 ext4_fsblk_t leaf_block_addr;
716
717                 rc = ext4_fs_get_inode_data_block_index(
718                     inode_ref, leaf_block_idx, &leaf_block_addr,
719                     false);
720                 if (rc != EOK)
721                         goto cleanup;
722
723                 struct ext4_block leaf_block;
724                 rc = ext4_block_get(fs->bdev, &leaf_block, leaf_block_addr);
725                 if (rc != EOK)
726                         goto cleanup;
727
728                 /* Linear search inside block */
729                 struct ext4_directory_entry_ll *res_dentry;
730                 rc = ext4_dir_find_in_block(&leaf_block, &fs->sb, name_len,
731                                             name, &res_dentry);
732
733                 /* Found => return it */
734                 if (rc == EOK) {
735                         result->block = leaf_block;
736                         result->dentry = res_dentry;
737                         goto cleanup;
738                 }
739
740                 /* Not found, leave untouched */
741                 rc2 = ext4_block_set(fs->bdev, &leaf_block);
742                 if (rc2 != EOK)
743                         goto cleanup;
744
745                 if (rc != ENOENT)
746                         goto cleanup;
747
748                 /* check if the next block could be checked */
749                 rc = ext4_dir_dx_next_block(inode_ref, hinfo.hash, dx_block,
750                                             &dx_blocks[0]);
751                 if (rc < 0)
752                         goto cleanup;
753         } while (rc == ENOENT);
754
755         /* Entry not found */
756         rc = ENOENT;
757
758 cleanup:
759         /* The whole path must be released (preventing memory leak) */
760         tmp = dx_blocks;
761
762         while (tmp <= dx_block) {
763                 rc2 = ext4_block_set(fs->bdev, &tmp->block);
764                 if (rc == EOK && rc2 != EOK)
765                         rc = rc2;
766                 ++tmp;
767         }
768
769         return rc;
770 }
771
772 #if CONFIG_DIR_INDEX_COMB_SORT
773 #define SWAP_ENTRY(se1, se2)                                                   \
774         do {                                                                   \
775                 struct ext4_dx_sort_entry tmp = se1;                           \
776                 se1 = se2;                                                     \
777                 se2 = tmp;                                                     \
778         \
779 } while (0)
780
781 static void comb_sort(struct ext4_dx_sort_entry *se, uint32_t count)
782 {
783         struct ext4_dx_sort_entry *p, *q, *top = se + count - 1;
784         bool more;
785         /* Combsort */
786         while (count > 2) {
787                 count = (count * 10) / 13;
788                 if (count - 9 < 2)
789                         count = 11;
790                 for (p = top, q = p - count; q >= se; p--, q--)
791                         if (p->hash < q->hash)
792                                 SWAP_ENTRY(*p, *q);
793         }
794         /* Bubblesort */
795         do {
796                 more = 0;
797                 q = top;
798                 while (q-- > se) {
799                         if (q[1].hash >= q[0].hash)
800                                 continue;
801                         SWAP_ENTRY(*(q + 1), *q);
802                         more = 1;
803                 }
804         } while (more);
805 }
806 #else
807
808 /**@brief  Compare function used to pass in quicksort implementation.
809  *         It can compare two entries by hash value.
810  * @param arg1  First entry
811  * @param arg2  Second entry
812  * @param dummy Unused parameter, can be NULL
813  *
814  * @return Classic compare result
815  *         (0: equal, -1: arg1 < arg2, 1: arg1 > arg2)
816  */
817 static int ext4_dir_dx_entry_comparator(const void *arg1, const void *arg2)
818 {
819         struct ext4_dx_sort_entry *entry1 = (void *)arg1;
820         struct ext4_dx_sort_entry *entry2 = (void *)arg2;
821
822         if (entry1->hash == entry2->hash)
823                 return 0;
824
825         if (entry1->hash < entry2->hash)
826                 return -1;
827         else
828                 return 1;
829 }
830 #endif
831
832 /**@brief  Insert new index entry to block.
833  *         Note that space for new entry must be checked by caller.
834  * @param inode_ref   Directory i-node
835  * @param index_block Block where to insert new entry
836  * @param hash        Hash value covered by child node
837  * @param iblock      Logical number of child block
838  *
839  */
840 static void
841 ext4_dir_dx_insert_entry(struct ext4_inode_ref *inode_ref,
842                          struct ext4_directory_dx_block *index_block,
843                          uint32_t hash, uint32_t iblock)
844 {
845         struct ext4_directory_dx_entry *old_index_entry = index_block->position;
846         struct ext4_directory_dx_entry *new_index_entry = old_index_entry + 1;
847
848         struct ext4_directory_dx_countlimit *countlimit =
849             (struct ext4_directory_dx_countlimit *)index_block->entries;
850         uint32_t count = ext4_dir_dx_countlimit_get_count(countlimit);
851
852         struct ext4_directory_dx_entry *start_index = index_block->entries;
853         size_t bytes =
854             (uint8_t *)(start_index + count) - (uint8_t *)(new_index_entry);
855
856         memmove(new_index_entry + 1, new_index_entry, bytes);
857
858         ext4_dir_dx_entry_set_block(new_index_entry, iblock);
859         ext4_dir_dx_entry_set_hash(new_index_entry, hash);
860
861         ext4_dir_dx_countlimit_set_count(countlimit, count + 1);
862
863         ext4_dir_set_dx_checksum(inode_ref,
864                         (struct ext4_directory_entry_ll *)index_block->block.data);
865         index_block->block.dirty = true;
866 }
867
868 /**@brief Split directory entries to two parts preventing node overflow.
869  * @param inode_ref      Directory i-node
870  * @param hinfo          Hash info
871  * @param old_data_block Block with data to be split
872  * @param index_block    Block where index entries are located
873  * @param new_data_block Output value for newly allocated data block
874  */
875 static int ext4_dir_dx_split_data(struct ext4_inode_ref *inode_ref,
876                                   struct ext4_hash_info *hinfo,
877                                   struct ext4_block *old_data_block,
878                                   struct ext4_directory_dx_block *index_block,
879                                   struct ext4_block *new_data_block)
880 {
881         int rc = EOK;
882
883         /* Allocate buffer for directory entries */
884         uint32_t block_size = ext4_sb_get_block_size(&inode_ref->fs->sb);
885
886         uint8_t *entry_buffer = malloc(block_size);
887         if (entry_buffer == NULL)
888                 return ENOMEM;
889
890         /* dot entry has the smallest size available */
891         uint32_t max_entry_count =
892             block_size / sizeof(struct ext4_directory_dx_dot_entry);
893
894         /* Allocate sort entry */
895         struct ext4_dx_sort_entry *sort_array =
896             malloc(max_entry_count * sizeof(struct ext4_dx_sort_entry));
897
898         if (sort_array == NULL) {
899                 free(entry_buffer);
900                 return ENOMEM;
901         }
902
903         uint32_t idx = 0;
904         uint32_t real_size = 0;
905
906         /* Initialize hinfo */
907         struct ext4_hash_info tmp_hinfo;
908         memcpy(&tmp_hinfo, hinfo, sizeof(struct ext4_hash_info));
909
910         /* Load all valid entries to the buffer */
911         struct ext4_directory_entry_ll *dentry = (void *)old_data_block->data;
912         uint8_t *entry_buffer_ptr = entry_buffer;
913         while ((void *)dentry < (void *)(old_data_block->data + block_size)) {
914                 /* Read only valid entries */
915                 if (ext4_dir_entry_ll_get_inode(dentry) &&
916                     dentry->name_length) {
917                         uint8_t len = ext4_dir_entry_ll_get_name_length(
918                             &inode_ref->fs->sb, dentry);
919
920                         rc = ext4_dir_dx_hash_string(&tmp_hinfo, len,
921                                                      (char *)dentry->name);
922                         if (rc != EOK) {
923                                 free(sort_array);
924                                 free(entry_buffer);
925                                 return rc;
926                         }
927
928                         uint32_t rec_len = 8 + len;
929
930                         if ((rec_len % 4) != 0)
931                                 rec_len += 4 - (rec_len % 4);
932
933                         memcpy(entry_buffer_ptr, dentry, rec_len);
934
935                         sort_array[idx].dentry = entry_buffer_ptr;
936                         sort_array[idx].rec_len = rec_len;
937                         sort_array[idx].hash = tmp_hinfo.hash;
938
939                         entry_buffer_ptr += rec_len;
940                         real_size += rec_len;
941                         idx++;
942                 }
943
944                 dentry = (void *)((uint8_t *)dentry +
945                                   ext4_dir_entry_ll_get_entry_length(dentry));
946         }
947
948 /* Sort all entries */
949 #if CONFIG_DIR_INDEX_COMB_SORT
950         comb_sort(sort_array, idx);
951 #else
952         qsort(sort_array, idx, sizeof(struct ext4_dx_sort_entry),
953               ext4_dir_dx_entry_comparator);
954 #endif
955         /* Allocate new block for store the second part of entries */
956         ext4_fsblk_t new_fblock;
957         uint32_t new_iblock;
958         rc = ext4_fs_append_inode_block(inode_ref, &new_fblock, &new_iblock);
959         if (rc != EOK) {
960                 free(sort_array);
961                 free(entry_buffer);
962                 return rc;
963         }
964
965         /* Load new block */
966         struct ext4_block new_data_block_tmp;
967         rc = ext4_block_get(inode_ref->fs->bdev, &new_data_block_tmp,
968                             new_fblock);
969         if (rc != EOK) {
970                 free(sort_array);
971                 free(entry_buffer);
972                 return rc;
973         }
974
975         /*
976          * Distribute entries to two blocks (by size)
977          * - compute the half
978          */
979         uint32_t new_hash = 0;
980         uint32_t current_size = 0;
981         uint32_t mid = 0;
982         uint32_t i;
983         for (i = 0; i < idx; ++i) {
984                 if ((current_size + sort_array[i].rec_len) > (block_size / 2)) {
985                         new_hash = sort_array[i].hash;
986                         mid = i;
987                         break;
988                 }
989
990                 current_size += sort_array[i].rec_len;
991         }
992
993         /* Check hash collision */
994         uint32_t continued = 0;
995         if (new_hash == sort_array[mid - 1].hash)
996                 continued = 1;
997
998         uint32_t offset = 0;
999         void *ptr;
1000
1001         /* First part - to the old block */
1002         for (i = 0; i < mid; ++i) {
1003                 ptr = old_data_block->data + offset;
1004                 memcpy(ptr, sort_array[i].dentry, sort_array[i].rec_len);
1005
1006                 struct ext4_directory_entry_ll *tmp = ptr;
1007                 if (i < (mid - 1))
1008                         ext4_dir_entry_ll_set_entry_length(
1009                             tmp, sort_array[i].rec_len);
1010                 else
1011                         ext4_dir_entry_ll_set_entry_length(tmp,
1012                                                            block_size - offset);
1013
1014                 offset += sort_array[i].rec_len;
1015         }
1016
1017         /* Second part - to the new block */
1018         offset = 0;
1019         for (i = mid; i < idx; ++i) {
1020                 ptr = new_data_block_tmp.data + offset;
1021                 memcpy(ptr, sort_array[i].dentry, sort_array[i].rec_len);
1022
1023                 struct ext4_directory_entry_ll *tmp = ptr;
1024                 if (i < (idx - 1))
1025                         ext4_dir_entry_ll_set_entry_length(
1026                             tmp, sort_array[i].rec_len);
1027                 else
1028                         ext4_dir_entry_ll_set_entry_length(tmp,
1029                                                            block_size - offset);
1030
1031                 offset += sort_array[i].rec_len;
1032         }
1033
1034         /* Do some steps to finish operation */
1035         ext4_dir_set_dx_checksum(inode_ref,
1036                         (struct ext4_directory_entry_ll *)old_data_block->data);
1037         ext4_dir_set_dx_checksum(inode_ref,
1038                         (struct ext4_directory_entry_ll *)new_data_block_tmp.data);
1039         old_data_block->dirty = true;
1040         new_data_block_tmp.dirty = true;
1041
1042         free(sort_array);
1043         free(entry_buffer);
1044
1045         ext4_dir_dx_insert_entry(inode_ref,
1046                         index_block,
1047                         new_hash + continued, new_iblock);
1048
1049         *new_data_block = new_data_block_tmp;
1050
1051         return EOK;
1052 }
1053
1054 /**@brief  Split index node and maybe some parent nodes in the tree hierarchy.
1055  * @param inode_ref Directory i-node
1056  * @param dx_blocks Array with path from root to leaf node
1057  * @param dx_block  Leaf block to be split if needed
1058  * @return Error code
1059  */
1060 static int
1061 ext4_dir_dx_split_index(struct ext4_inode_ref *inode_ref,
1062                         struct ext4_directory_dx_block *dx_blocks,
1063                         struct ext4_directory_dx_block *dx_block,
1064                         struct ext4_directory_dx_block **new_dx_block)
1065 {
1066         struct ext4_directory_dx_entry *entries;
1067
1068         if (dx_block == dx_blocks)
1069                 entries =
1070                     ((struct ext4_directory_dx_root *)dx_block->block.data)
1071                         ->entries;
1072         else
1073                 entries =
1074                     ((struct ext4_directory_dx_node *)dx_block->block.data)
1075                         ->entries;
1076
1077         struct ext4_directory_dx_countlimit *countlimit =
1078             (struct ext4_directory_dx_countlimit *)entries;
1079
1080         uint16_t leaf_limit = ext4_dir_dx_countlimit_get_limit(countlimit);
1081         uint16_t leaf_count = ext4_dir_dx_countlimit_get_count(countlimit);
1082
1083         /* Check if is necessary to split index block */
1084         if (leaf_limit == leaf_count) {
1085                 size_t levels = dx_block - dx_blocks;
1086
1087                 struct ext4_directory_dx_entry *root_entries =
1088                     ((struct ext4_directory_dx_root *)dx_blocks[0].block.data)
1089                         ->entries;
1090
1091                 struct ext4_directory_dx_countlimit *root_countlimit =
1092                     (struct ext4_directory_dx_countlimit *)root_entries;
1093                 uint16_t root_limit =
1094                     ext4_dir_dx_countlimit_get_limit(root_countlimit);
1095                 uint16_t root_count =
1096                     ext4_dir_dx_countlimit_get_count(root_countlimit);
1097
1098                 /* Linux limitation */
1099                 if ((levels > 0) && (root_limit == root_count))
1100                         return ENOSPC;
1101
1102                 /* Add new block to directory */
1103                 ext4_fsblk_t new_fblock;
1104                 uint32_t new_iblock;
1105                 int rc = ext4_fs_append_inode_block(inode_ref, &new_fblock,
1106                                                     &new_iblock);
1107                 if (rc != EOK)
1108                         return rc;
1109
1110                 /* load new block */
1111                 struct ext4_block new_block;
1112                 rc =
1113                     ext4_block_get(inode_ref->fs->bdev, &new_block, new_fblock);
1114                 if (rc != EOK)
1115                         return rc;
1116
1117                 struct ext4_directory_dx_node *new_node =
1118                     (void *)new_block.data;
1119                 struct ext4_directory_dx_entry *new_entries = new_node->entries;
1120
1121                 memset(&new_node->fake, 0,
1122                        sizeof(struct ext4_fake_directory_entry));
1123
1124                 uint32_t block_size =
1125                     ext4_sb_get_block_size(&inode_ref->fs->sb);
1126
1127                 new_node->fake.entry_length = block_size;
1128
1129                 /* Split leaf node */
1130                 if (levels > 0) {
1131                         uint32_t count_left = leaf_count / 2;
1132                         uint32_t count_right = leaf_count - count_left;
1133                         uint32_t hash_right =
1134                             ext4_dir_dx_entry_get_hash(entries + count_left);
1135
1136                         /* Copy data to new node */
1137                         memcpy((void *)new_entries,
1138                                (void *)(entries + count_left),
1139                                count_right *
1140                                    sizeof(struct ext4_directory_dx_entry));
1141
1142                         /* Initialize new node */
1143                         struct ext4_directory_dx_countlimit *left_countlimit =
1144                             (struct ext4_directory_dx_countlimit *)entries;
1145                         struct ext4_directory_dx_countlimit *right_countlimit =
1146                             (struct ext4_directory_dx_countlimit *)new_entries;
1147
1148                         ext4_dir_dx_countlimit_set_count(left_countlimit,
1149                                                          count_left);
1150                         ext4_dir_dx_countlimit_set_count(right_countlimit,
1151                                                          count_right);
1152
1153                         uint32_t entry_space =
1154                             block_size -
1155                             sizeof(struct ext4_fake_directory_entry);
1156                         uint32_t node_limit =
1157                             entry_space /
1158                             sizeof(struct ext4_directory_dx_entry);
1159                         ext4_dir_dx_countlimit_set_limit(right_countlimit,
1160                                                          node_limit);
1161
1162                         /* Which index block is target for new entry */
1163                         uint32_t position_index =
1164                             (dx_block->position - dx_block->entries);
1165                         if (position_index >= count_left) {
1166                                 ext4_dir_set_dx_checksum(
1167                                                 inode_ref,
1168                                                 (struct ext4_directory_entry_ll *)
1169                                                 dx_block->block.data);
1170                                 dx_block->block.dirty = true;
1171
1172                                 struct ext4_block block_tmp = dx_block->block;
1173
1174                                 dx_block->block = new_block;
1175
1176                                 dx_block->position =
1177                                     new_entries + position_index - count_left;
1178                                 dx_block->entries = new_entries;
1179
1180                                 new_block = block_tmp;
1181                         }
1182
1183                         /* Finally insert new entry */
1184                         ext4_dir_dx_insert_entry(inode_ref,
1185                                                  dx_blocks, hash_right,
1186                                                  new_iblock);
1187                         ext4_dir_set_dx_checksum(inode_ref,
1188                                                 (struct ext4_directory_entry_ll *)
1189                                                         dx_blocks[0].block.data);
1190                         ext4_dir_set_dx_checksum(inode_ref,
1191                                                 (struct ext4_directory_entry_ll *)
1192                                                         dx_blocks[1].block.data);
1193                         dx_blocks[0].block.dirty = true;
1194                         dx_blocks[1].block.dirty = true;
1195
1196                         ext4_dir_set_dx_checksum(inode_ref,
1197                                                 (struct ext4_directory_entry_ll *)
1198                                                         new_block.data);
1199                         new_block.dirty = true;
1200                         return ext4_block_set(inode_ref->fs->bdev, &new_block);
1201                 } else {
1202                         /* Create second level index */
1203
1204                         /* Copy data from root to child block */
1205                         memcpy((void *)new_entries, (void *)entries,
1206                                leaf_count *
1207                                    sizeof(struct ext4_directory_dx_entry));
1208
1209                         struct ext4_directory_dx_countlimit *new_countlimit =
1210                             (struct ext4_directory_dx_countlimit *)new_entries;
1211
1212                         uint32_t entry_space =
1213                             block_size -
1214                             sizeof(struct ext4_fake_directory_entry);
1215                         uint32_t node_limit =
1216                             entry_space /
1217                             sizeof(struct ext4_directory_dx_entry);
1218                         ext4_dir_dx_countlimit_set_limit(new_countlimit,
1219                                                          node_limit);
1220
1221                         /* Set values in root node */
1222                         struct ext4_directory_dx_countlimit
1223                             *new_root_countlimit =
1224                                 (struct ext4_directory_dx_countlimit *)entries;
1225
1226                         ext4_dir_dx_countlimit_set_count(new_root_countlimit,
1227                                                          1);
1228                         ext4_dir_dx_entry_set_block(entries, new_iblock);
1229
1230                         ((struct ext4_directory_dx_root *)dx_blocks[0]
1231                              .block.data)
1232                             ->info.indirect_levels = 1;
1233
1234                         /* Add new entry to the path */
1235                         dx_block = dx_blocks + 1;
1236                         dx_block->position =
1237                             dx_blocks->position - entries + new_entries;
1238                         dx_block->entries = new_entries;
1239                         dx_block->block = new_block;
1240
1241                         *new_dx_block = dx_block;
1242
1243                         ext4_dir_set_dx_checksum(inode_ref,
1244                                                 (struct ext4_directory_entry_ll *)
1245                                                         dx_blocks[0].block.data);
1246                         ext4_dir_set_dx_checksum(inode_ref,
1247                                                 (struct ext4_directory_entry_ll *)
1248                                                         dx_blocks[1].block.data);
1249                         dx_blocks[0].block.dirty = true;
1250                         dx_blocks[1].block.dirty = true;
1251                 }
1252         }
1253
1254         return EOK;
1255 }
1256
1257 int ext4_dir_dx_add_entry(struct ext4_inode_ref *parent,
1258                           struct ext4_inode_ref *child, const char *name)
1259 {
1260         int rc2 = EOK;
1261
1262         /* Get direct block 0 (index root) */
1263         ext4_fsblk_t root_block_addr;
1264         int rc =
1265             ext4_fs_get_inode_data_block_index(parent,
1266                             0, &root_block_addr,
1267                             false);
1268         if (rc != EOK)
1269                 return rc;
1270
1271         struct ext4_fs *fs = parent->fs;
1272         struct ext4_block root_block;
1273
1274         rc = ext4_block_get(fs->bdev, &root_block, root_block_addr);
1275         if (rc != EOK)
1276                 return rc;
1277
1278         /* Initialize hinfo structure (mainly compute hash) */
1279         uint32_t name_len = strlen(name);
1280         struct ext4_hash_info hinfo;
1281         rc = ext4_dir_hinfo_init(&hinfo, &root_block, &fs->sb, name_len, name);
1282         if (rc != EOK) {
1283                 ext4_block_set(fs->bdev, &root_block);
1284                 return EXT4_ERR_BAD_DX_DIR;
1285         }
1286
1287         /*
1288          * Hardcoded number 2 means maximum height of index
1289          * tree defined in Linux.
1290          */
1291         struct ext4_directory_dx_block dx_blocks[2];
1292         struct ext4_directory_dx_block *dx_block;
1293         struct ext4_directory_dx_block *dx_it;
1294
1295         rc = ext4_dir_dx_get_leaf(&hinfo, parent, &root_block, &dx_block,
1296                                   dx_blocks);
1297         if (rc != EOK) {
1298                 rc = EXT4_ERR_BAD_DX_DIR;
1299                 goto release_index;
1300         }
1301
1302         /* Try to insert to existing data block */
1303         uint32_t leaf_block_idx =
1304             ext4_dir_dx_entry_get_block(dx_block->position);
1305         ext4_fsblk_t leaf_block_addr;
1306         rc = ext4_fs_get_inode_data_block_index(parent, leaf_block_idx,
1307                                                 &leaf_block_addr,
1308                                                 false);
1309         if (rc != EOK)
1310                 goto release_index;
1311
1312         /*
1313          * Check if there is needed to split index node
1314          * (and recursively also parent nodes)
1315          */
1316         rc = ext4_dir_dx_split_index(parent, dx_blocks, dx_block, &dx_block);
1317         if (rc != EOK)
1318                 goto release_target_index;
1319
1320         struct ext4_block target_block;
1321         rc = ext4_block_get(fs->bdev, &target_block, leaf_block_addr);
1322         if (rc != EOK)
1323                 goto release_index;
1324
1325         /* Check if insert operation passed */
1326         rc = ext4_dir_try_insert_entry(&fs->sb, parent, &target_block, child, name,
1327                                        name_len);
1328         if (rc == EOK)
1329                 goto release_target_index;
1330
1331         /* Split entries to two blocks (includes sorting by hash value) */
1332         struct ext4_block new_block;
1333         rc = ext4_dir_dx_split_data(parent, &hinfo, &target_block, dx_block,
1334                                     &new_block);
1335         if (rc != EOK) {
1336                 rc2 = rc;
1337                 goto release_target_index;
1338         }
1339
1340         /* Where to save new entry */
1341         uint32_t new_block_hash =
1342             ext4_dir_dx_entry_get_hash(dx_block->position + 1);
1343         if (hinfo.hash >= new_block_hash)
1344                 rc = ext4_dir_try_insert_entry(&fs->sb, parent, &new_block, child, name,
1345                                                name_len);
1346         else
1347                 rc = ext4_dir_try_insert_entry(&fs->sb, parent, &target_block, child,
1348                                                name, name_len);
1349
1350         /* Cleanup */
1351         rc = ext4_block_set(fs->bdev, &new_block);
1352         if (rc != EOK)
1353                 return rc;
1354
1355 /* Cleanup operations */
1356
1357 release_target_index:
1358         rc2 = rc;
1359
1360         rc = ext4_block_set(fs->bdev, &target_block);
1361         if (rc != EOK)
1362                 return rc;
1363
1364 release_index:
1365         if (rc != EOK)
1366                 rc2 = rc;
1367
1368         dx_it = dx_blocks;
1369
1370         while (dx_it <= dx_block) {
1371                 rc = ext4_block_set(fs->bdev, &dx_it->block);
1372                 if (rc != EOK)
1373                         return rc;
1374
1375                 dx_it++;
1376         }
1377
1378         return rc2;
1379 }
1380
1381 int ext4_dir_dx_reset_parent_inode(struct ext4_inode_ref *dir,
1382                                    uint32_t parent_inode)
1383 {
1384         /* Load block 0, where will be index root located */
1385         ext4_fsblk_t fblock;
1386         int rc = ext4_fs_get_inode_data_block_index(dir, 0, &fblock, false);
1387         if (rc != EOK)
1388                 return rc;
1389
1390         struct ext4_block block;
1391         rc = ext4_block_get(dir->fs->bdev, &block, fblock);
1392         if (rc != EOK)
1393                 return rc;
1394
1395         /* Initialize pointers to data structures */
1396         struct ext4_directory_dx_root *root = (void *)block.data;
1397
1398         /* Fill the inode field with a new parent ino. */
1399         ext4_dx_dot_entry_set_inode(&root->dots[1], parent_inode);
1400
1401         ext4_dir_set_dx_checksum(dir,
1402                                 (struct ext4_directory_entry_ll *)
1403                                         block.data);
1404         block.dirty = true;
1405
1406         return ext4_block_set(dir->fs->bdev, &block);
1407 }
1408
1409 /**
1410  * @}
1411  */