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