Code format in dir_idx module
[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_dir_idx_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_dir_idx_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_dir_idx_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_dir_idx_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_dir_idx_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_dir_idx_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_dir_idx_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_dir_idx_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_dir_idx_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_dir_idx_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_dir_idx_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_dir_idx_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_dir_idx_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_dir_idx_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_dir_idx_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_dir_idx_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_dir_idx_tail));
241                 t->checksum = orig_checksum;
242         }
243         return checksum;
244 }
245
246 static struct ext4_dir_idx_countlimit *
247 ext4_dir_dx_get_countlimit(struct ext4_inode_ref *inode_ref,
248                         struct ext4_dir_entry_ll *dirent,
249                         int *offset)
250 {
251         struct ext4_dir_entry_ll *dp;
252         struct ext4_dir_idx_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_dir_idx_root *)dirent;
261                 dp = (struct ext4_dir_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_dir_idx_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_dir_idx_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_dir_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_dir_idx_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_dir_idx_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_dir_idx_entry)) >
299                                 ext4_sb_get_block_size(sb) -
300                                 sizeof(struct ext4_dir_idx_tail)) {
301                         /* There is no space to hold the checksum */
302                         return true;
303                 }
304                 t = (void *)(((struct ext4_dir_idx_entry *)countlimit) + limit);
305
306                 if (t->checksum != to_le32(ext4_dir_dx_checksum(inode_ref,
307                                                                 dirent,
308                                                                 count_offset,
309                                                                 count, t)))
310                         return false;
311         }
312         return true;
313 }
314
315
316 static void
317 ext4_dir_set_dx_checksum(struct ext4_inode_ref *inode_ref,
318                         struct ext4_dir_entry_ll *dirent)
319 {
320         int count_offset, limit, count;
321         struct ext4_sblock *sb = &inode_ref->fs->sb;
322
323         if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
324                 struct ext4_dir_idx_countlimit *countlimit =
325                         ext4_dir_dx_get_countlimit(inode_ref, dirent, &count_offset);
326                 if (!countlimit) {
327                         /* Directory seems corrupted. */
328                         return;
329                 }
330                 struct ext4_dir_idx_tail *t;
331                 limit = ext4_dir_dx_countlimit_get_limit(countlimit);
332                 count = ext4_dir_dx_countlimit_get_count(countlimit);
333                 if (count_offset + (limit * sizeof(struct ext4_dir_idx_entry)) >
334                                 ext4_sb_get_block_size(sb) -
335                                 sizeof(struct ext4_dir_idx_tail)) {
336                         /* There is no space to hold the checksum */
337                         return;
338                 }
339                 t = (struct ext4_dir_idx_tail *)
340                         (((struct ext4_dir_idx_entry *)countlimit) + limit);
341
342                 t->checksum = to_le32(ext4_dir_dx_checksum(inode_ref, dirent,
343                                         count_offset, count, t));
344         }
345 }
346 #else
347 #define ext4_dir_dx_checksum_verify(...) true
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_dir_idx_root *root = (void *)block.data;
370         struct ext4_dir_idx_root_info *info = &(root->info);
371
372         /* Initialize dot entries */
373         ext4_dir_write_entry(&dir->fs->sb,
374                         (struct ext4_dir_entry_ll *)root->dots,
375                         12,
376                         dir,
377                         ".",
378                         strlen("."));
379
380         ext4_dir_write_entry(&dir->fs->sb,
381                         (struct ext4_dir_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_dir_idx_countlimit *countlimit =
396             (struct ext4_dir_idx_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_dir_idx_dot_entry) -
403                                sizeof(struct ext4_dir_idx_root_info);
404         if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
405                 entry_space -= sizeof(struct ext4_dir_idx_tail);
406
407         uint16_t root_limit =
408             entry_space / sizeof(struct ext4_dir_idx_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_dir_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_dir_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_dir_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_dir_idx_entry *entry = root->entries;
460         ext4_dir_dx_entry_set_block(entry, iblock);
461
462         ext4_dir_set_dx_checksum(dir,
463                         (struct ext4_dir_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_dir_idx_root *root =
483             (struct ext4_dir_idx_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_dir_idx_dot_entry);
502         entry_space -= sizeof(struct ext4_dir_idx_root_info);
503         if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
504                 entry_space -= sizeof(struct ext4_dir_idx_tail);
505         entry_space = entry_space / sizeof(struct ext4_dir_idx_entry);
506
507         uint16_t limit = ext4_dir_dx_countlimit_get_limit(
508             (struct ext4_dir_idx_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_dir_idx_block **dx_block,
544                                 struct ext4_dir_idx_block *dx_blocks)
545 {
546         struct ext4_dir_idx_block *tmp_dx_block = dx_blocks;
547         struct ext4_dir_idx_root *root =
548             (struct ext4_dir_idx_root *)root_block->data;
549         struct ext4_dir_idx_entry *entries =
550             (struct ext4_dir_idx_entry *)&root->entries;
551
552         uint16_t limit = ext4_dir_dx_countlimit_get_limit(
553             (struct ext4_dir_idx_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_dir_idx_entry *p;
559         struct ext4_dir_idx_entry *q;
560         struct ext4_dir_idx_entry *m;
561         struct ext4_dir_idx_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_dir_idx_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_dir_idx_node *)tmp_block->data)->entries;
614                 limit = ext4_dir_dx_countlimit_get_limit(
615                     (struct ext4_dir_idx_countlimit *)entries);
616
617                 struct ext4_sblock *sb = &inode_ref->fs->sb;
618                 uint16_t entry_space = ext4_sb_get_block_size(sb) -
619                                 sizeof(struct ext4_fake_dir_entry);
620
621                 if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
622                         entry_space -= sizeof(struct ext4_dir_idx_tail);
623
624                 entry_space = entry_space / sizeof(struct ext4_dir_idx_entry);
625
626                 if (limit != entry_space) {
627                         ext4_block_set(inode_ref->fs->bdev, tmp_block);
628                         return EXT4_ERR_BAD_DX_DIR;
629                 }
630
631                 if (!ext4_dir_dx_checksum_verify(inode_ref,
632                                 (struct ext4_dir_entry_ll *)tmp_block->data)) {
633                         ext4_dbg(DEBUG_DIR_IDX,
634                                         DBG_WARN "HTree checksum failed."
635                                         "Inode: %" PRIu32", "
636                                         "Block: %" PRIu32"\n",
637                                         inode_ref->index,
638                                         next_block);
639                 }
640
641                 ++tmp_dx_block;
642         }
643
644         /* Unreachable */
645         return EOK;
646 }
647
648 /**@brief Check if the the next block would be checked during entry search.
649  * @param inode_ref Directory i-node
650  * @param hash      Hash value to check
651  * @param dx_block  Current block
652  * @param dx_blocks Array with path from root to leaf node
653  * @return Standard Error code
654  */
655 static int ext4_dir_dx_next_block(struct ext4_inode_ref *inode_ref,
656                                   uint32_t hash,
657                                   struct ext4_dir_idx_block *dx_block,
658                                   struct ext4_dir_idx_block *dx_blocks)
659 {
660         uint32_t num_handles = 0;
661         struct ext4_dir_idx_block *p = dx_block;
662
663         /* Try to find data block with next bunch of entries */
664         while (true) {
665                 p->position++;
666                 uint16_t count = ext4_dir_dx_countlimit_get_count(
667                     (struct ext4_dir_idx_countlimit *)p->entries);
668
669                 if (p->position < p->entries + count)
670                         break;
671
672                 if (p == dx_blocks)
673                         return EOK;
674
675                 num_handles++;
676                 p--;
677         }
678
679         /* Check hash collision (if not occurred - no next block cannot be
680          * used)*/
681         uint32_t current_hash = ext4_dir_dx_entry_get_hash(p->position);
682         if ((hash & 1) == 0) {
683                 if ((current_hash & ~1) != hash)
684                         return 0;
685         }
686
687         /* Fill new path */
688         while (num_handles--) {
689                 uint32_t block_idx = ext4_dir_dx_entry_get_block(p->position);
690                 ext4_fsblk_t block_addr;
691
692                 int rc = ext4_fs_get_inode_data_block_index(
693                     inode_ref, block_idx, &block_addr, false);
694                 if (rc != EOK)
695                         return rc;
696
697                 struct ext4_block block;
698                 rc = ext4_block_get(inode_ref->fs->bdev, &block, block_addr);
699                 if (rc != EOK)
700                         return rc;
701
702                 if (!ext4_dir_dx_checksum_verify(inode_ref,
703                                 (struct ext4_dir_entry_ll *)block.data)) {
704                         ext4_dbg(DEBUG_DIR_IDX,
705                                         DBG_WARN "HTree checksum failed."
706                                         "Inode: %" PRIu32", "
707                                         "Block: %" PRIu32"\n",
708                                         inode_ref->index,
709                                         block_idx);
710                 }
711
712                 p++;
713
714                 /* Don't forget to put old block (prevent memory leak) */
715                 rc = ext4_block_set(inode_ref->fs->bdev, &p->block);
716                 if (rc != EOK)
717                         return rc;
718
719                 memcpy(&p->block, &block, sizeof(block));
720                 p->entries =
721                     ((struct ext4_dir_idx_node *)block.data)->entries;
722                 p->position = p->entries;
723         }
724
725         return ENOENT;
726 }
727
728 int ext4_dir_dx_find_entry(struct ext4_dir_search_result *result,
729                            struct ext4_inode_ref *inode_ref, size_t name_len,
730                            const char *name)
731 {
732         /* Load direct block 0 (index root) */
733         ext4_fsblk_t root_block_addr;
734         int rc2;
735         int rc =
736             ext4_fs_get_inode_data_block_index(inode_ref,
737                             0, &root_block_addr, false);
738         if (rc != EOK)
739                 return rc;
740
741         struct ext4_fs *fs = inode_ref->fs;
742
743         struct ext4_block root_block;
744         rc = ext4_block_get(fs->bdev, &root_block, root_block_addr);
745         if (rc != EOK)
746                 return rc;
747
748         if (!ext4_dir_dx_checksum_verify(inode_ref,
749                                 (struct ext4_dir_entry_ll *)root_block.data)) {
750                 ext4_dbg(DEBUG_DIR_IDX,
751                          DBG_WARN "HTree root checksum failed."
752                          "Inode: %" PRIu32", "
753                          "Block: %" PRIu32"\n",
754                          inode_ref->index,
755                          (uint32_t)0);
756         }
757
758         /* Initialize hash info (compute hash value) */
759         struct ext4_hash_info hinfo;
760         rc = ext4_dir_hinfo_init(&hinfo, &root_block, &fs->sb, name_len, name);
761         if (rc != EOK) {
762                 ext4_block_set(fs->bdev, &root_block);
763                 return EXT4_ERR_BAD_DX_DIR;
764         }
765
766         /*
767          * Hardcoded number 2 means maximum height of index tree,
768          * specified in the Linux driver.
769          */
770         struct ext4_dir_idx_block dx_blocks[2];
771         struct ext4_dir_idx_block *dx_block;
772         struct ext4_dir_idx_block *tmp;
773
774         rc = ext4_dir_dx_get_leaf(&hinfo, inode_ref, &root_block, &dx_block,
775                                   dx_blocks);
776         if (rc != EOK) {
777                 ext4_block_set(fs->bdev, &root_block);
778                 return EXT4_ERR_BAD_DX_DIR;
779         }
780
781         do {
782                 /* Load leaf block */
783                 uint32_t leaf_block_idx =
784                     ext4_dir_dx_entry_get_block(dx_block->position);
785                 ext4_fsblk_t leaf_block_addr;
786
787                 rc = ext4_fs_get_inode_data_block_index(
788                     inode_ref, leaf_block_idx, &leaf_block_addr,
789                     false);
790                 if (rc != EOK)
791                         goto cleanup;
792
793                 struct ext4_block leaf_block;
794                 rc = ext4_block_get(fs->bdev, &leaf_block, leaf_block_addr);
795                 if (rc != EOK)
796                         goto cleanup;
797
798                 if (!ext4_dir_checksum_verify(inode_ref,
799                                 (struct ext4_dir_entry_ll *)leaf_block.data)) {
800                         ext4_dbg(DEBUG_DIR_IDX,
801                                  DBG_WARN "HTree leaf block checksum failed."
802                                  "Inode: %" PRIu32", "
803                                  "Block: %" PRIu32"\n",
804                                  inode_ref->index,
805                                  leaf_block_idx);
806                 }
807
808                 /* Linear search inside block */
809                 struct ext4_dir_entry_ll *res_dentry;
810                 rc = ext4_dir_find_in_block(&leaf_block, &fs->sb, name_len,
811                                             name, &res_dentry);
812
813                 /* Found => return it */
814                 if (rc == EOK) {
815                         result->block = leaf_block;
816                         result->dentry = res_dentry;
817                         goto cleanup;
818                 }
819
820                 /* Not found, leave untouched */
821                 rc2 = ext4_block_set(fs->bdev, &leaf_block);
822                 if (rc2 != EOK)
823                         goto cleanup;
824
825                 if (rc != ENOENT)
826                         goto cleanup;
827
828                 /* check if the next block could be checked */
829                 rc = ext4_dir_dx_next_block(inode_ref, hinfo.hash, dx_block,
830                                             &dx_blocks[0]);
831                 if (rc < 0)
832                         goto cleanup;
833         } while (rc == ENOENT);
834
835         /* Entry not found */
836         rc = ENOENT;
837
838 cleanup:
839         /* The whole path must be released (preventing memory leak) */
840         tmp = dx_blocks;
841
842         while (tmp <= dx_block) {
843                 rc2 = ext4_block_set(fs->bdev, &tmp->block);
844                 if (rc == EOK && rc2 != EOK)
845                         rc = rc2;
846                 ++tmp;
847         }
848
849         return rc;
850 }
851
852 #if CONFIG_DIR_INDEX_COMB_SORT
853 #define SWAP_ENTRY(se1, se2)                                                   \
854         do {                                                                   \
855                 struct ext4_dx_sort_entry tmp = se1;                           \
856                 se1 = se2;                                                     \
857                 se2 = tmp;                                                     \
858         \
859 } while (0)
860
861 static void comb_sort(struct ext4_dx_sort_entry *se, uint32_t count)
862 {
863         struct ext4_dx_sort_entry *p, *q, *top = se + count - 1;
864         bool more;
865         /* Combsort */
866         while (count > 2) {
867                 count = (count * 10) / 13;
868                 if (count - 9 < 2)
869                         count = 11;
870                 for (p = top, q = p - count; q >= se; p--, q--)
871                         if (p->hash < q->hash)
872                                 SWAP_ENTRY(*p, *q);
873         }
874         /* Bubblesort */
875         do {
876                 more = 0;
877                 q = top;
878                 while (q-- > se) {
879                         if (q[1].hash >= q[0].hash)
880                                 continue;
881                         SWAP_ENTRY(*(q + 1), *q);
882                         more = 1;
883                 }
884         } while (more);
885 }
886 #else
887
888 /**@brief  Compare function used to pass in quicksort implementation.
889  *         It can compare two entries by hash value.
890  * @param arg1  First entry
891  * @param arg2  Second entry
892  * @param dummy Unused parameter, can be NULL
893  *
894  * @return Classic compare result
895  *         (0: equal, -1: arg1 < arg2, 1: arg1 > arg2)
896  */
897 static int ext4_dir_dx_entry_comparator(const void *arg1, const void *arg2)
898 {
899         struct ext4_dx_sort_entry *entry1 = (void *)arg1;
900         struct ext4_dx_sort_entry *entry2 = (void *)arg2;
901
902         if (entry1->hash == entry2->hash)
903                 return 0;
904
905         if (entry1->hash < entry2->hash)
906                 return -1;
907         else
908                 return 1;
909 }
910 #endif
911
912 /**@brief  Insert new index entry to block.
913  *         Note that space for new entry must be checked by caller.
914  * @param inode_ref   Directory i-node
915  * @param index_block Block where to insert new entry
916  * @param hash        Hash value covered by child node
917  * @param iblock      Logical number of child block
918  *
919  */
920 static void
921 ext4_dir_dx_insert_entry(struct ext4_inode_ref *inode_ref __unused,
922                          struct ext4_dir_idx_block *index_block,
923                          uint32_t hash, uint32_t iblock)
924 {
925         struct ext4_dir_idx_entry *old_index_entry = index_block->position;
926         struct ext4_dir_idx_entry *new_index_entry = old_index_entry + 1;
927
928         struct ext4_dir_idx_countlimit *countlimit =
929             (struct ext4_dir_idx_countlimit *)index_block->entries;
930         uint32_t count = ext4_dir_dx_countlimit_get_count(countlimit);
931
932         struct ext4_dir_idx_entry *start_index = index_block->entries;
933         size_t bytes =
934             (uint8_t *)(start_index + count) - (uint8_t *)(new_index_entry);
935
936         memmove(new_index_entry + 1, new_index_entry, bytes);
937
938         ext4_dir_dx_entry_set_block(new_index_entry, iblock);
939         ext4_dir_dx_entry_set_hash(new_index_entry, hash);
940
941         ext4_dir_dx_countlimit_set_count(countlimit, count + 1);
942
943         ext4_dir_set_dx_checksum(inode_ref,
944                         (struct ext4_dir_entry_ll *)index_block->block.data);
945         index_block->block.dirty = true;
946 }
947
948 /**@brief Split directory entries to two parts preventing node overflow.
949  * @param inode_ref      Directory i-node
950  * @param hinfo          Hash info
951  * @param old_data_block Block with data to be split
952  * @param index_block    Block where index entries are located
953  * @param new_data_block Output value for newly allocated data block
954  */
955 static int ext4_dir_dx_split_data(struct ext4_inode_ref *inode_ref,
956                                   struct ext4_hash_info *hinfo,
957                                   struct ext4_block *old_data_block,
958                                   struct ext4_dir_idx_block *index_block,
959                                   struct ext4_block *new_data_block)
960 {
961         int rc = EOK;
962
963         /* Allocate buffer for directory entries */
964         uint32_t block_size = ext4_sb_get_block_size(&inode_ref->fs->sb);
965
966         uint8_t *entry_buffer = malloc(block_size);
967         if (entry_buffer == NULL)
968                 return ENOMEM;
969
970         /* dot entry has the smallest size available */
971         uint32_t max_entry_count =
972             block_size / sizeof(struct ext4_dir_idx_dot_entry);
973
974         /* Allocate sort entry */
975         struct ext4_dx_sort_entry *sort_array =
976             malloc(max_entry_count * sizeof(struct ext4_dx_sort_entry));
977
978         if (sort_array == NULL) {
979                 free(entry_buffer);
980                 return ENOMEM;
981         }
982
983         uint32_t idx = 0;
984         uint32_t real_size = 0;
985
986         /* Initialize hinfo */
987         struct ext4_hash_info tmp_hinfo;
988         memcpy(&tmp_hinfo, hinfo, sizeof(struct ext4_hash_info));
989
990         /* Load all valid entries to the buffer */
991         struct ext4_dir_entry_ll *dentry = (void *)old_data_block->data;
992         uint8_t *entry_buffer_ptr = entry_buffer;
993         while ((void *)dentry < (void *)(old_data_block->data + block_size)) {
994                 /* Read only valid entries */
995                 if (ext4_dir_entry_ll_get_inode(dentry) && dentry->name_length) {
996                         uint8_t len = ext4_dir_entry_ll_get_name_length(
997                             &inode_ref->fs->sb, dentry);
998
999                         rc = ext4_dir_dx_hash_string(&tmp_hinfo, len,
1000                                                      (char *)dentry->name);
1001                         if (rc != EOK) {
1002                                 free(sort_array);
1003                                 free(entry_buffer);
1004                                 return rc;
1005                         }
1006
1007                         uint32_t rec_len = 8 + len;
1008
1009                         if ((rec_len % 4) != 0)
1010                                 rec_len += 4 - (rec_len % 4);
1011
1012                         memcpy(entry_buffer_ptr, dentry, rec_len);
1013
1014                         sort_array[idx].dentry = entry_buffer_ptr;
1015                         sort_array[idx].rec_len = rec_len;
1016                         sort_array[idx].hash = tmp_hinfo.hash;
1017
1018                         entry_buffer_ptr += rec_len;
1019                         real_size += rec_len;
1020                         idx++;
1021                 }
1022
1023                 dentry = (void *)((uint8_t *)dentry +
1024                                   ext4_dir_entry_ll_get_entry_length(dentry));
1025         }
1026
1027 /* Sort all entries */
1028 #if CONFIG_DIR_INDEX_COMB_SORT
1029         comb_sort(sort_array, idx);
1030 #else
1031         qsort(sort_array, idx, sizeof(struct ext4_dx_sort_entry),
1032               ext4_dir_dx_entry_comparator);
1033 #endif
1034         /* Allocate new block for store the second part of entries */
1035         ext4_fsblk_t new_fblock;
1036         uint32_t new_iblock;
1037         rc = ext4_fs_append_inode_block(inode_ref, &new_fblock, &new_iblock);
1038         if (rc != EOK) {
1039                 free(sort_array);
1040                 free(entry_buffer);
1041                 return rc;
1042         }
1043
1044         /* Load new block */
1045         struct ext4_block new_data_block_tmp;
1046         rc = ext4_block_get(inode_ref->fs->bdev, &new_data_block_tmp,
1047                             new_fblock);
1048         if (rc != EOK) {
1049                 free(sort_array);
1050                 free(entry_buffer);
1051                 return rc;
1052         }
1053
1054         /*
1055          * Distribute entries to two blocks (by size)
1056          * - compute the half
1057          */
1058         uint32_t new_hash = 0;
1059         uint32_t current_size = 0;
1060         uint32_t mid = 0;
1061         uint32_t i;
1062         for (i = 0; i < idx; ++i) {
1063                 if ((current_size + sort_array[i].rec_len) > (block_size / 2)) {
1064                         new_hash = sort_array[i].hash;
1065                         mid = i;
1066                         break;
1067                 }
1068
1069                 current_size += sort_array[i].rec_len;
1070         }
1071
1072         /* Check hash collision */
1073         uint32_t continued = 0;
1074         if (new_hash == sort_array[mid - 1].hash)
1075                 continued = 1;
1076
1077         uint32_t offset = 0;
1078         void *ptr;
1079         struct ext4_sblock *sb = &inode_ref->fs->sb;
1080         if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
1081                 block_size -= sizeof(struct ext4_dir_entry_tail);
1082
1083         /* First part - to the old block */
1084         for (i = 0; i < mid; ++i) {
1085                 ptr = old_data_block->data + offset;
1086                 memcpy(ptr, sort_array[i].dentry, sort_array[i].rec_len);
1087
1088                 struct ext4_dir_entry_ll *tmp = ptr;
1089                 if (i < (mid - 1))
1090                         ext4_dir_entry_ll_set_entry_length(
1091                             tmp, sort_array[i].rec_len);
1092                 else
1093                         ext4_dir_entry_ll_set_entry_length(tmp,
1094                                                            block_size - offset);
1095
1096                 offset += sort_array[i].rec_len;
1097         }
1098
1099         /* Second part - to the new block */
1100         offset = 0;
1101         for (i = mid; i < idx; ++i) {
1102                 ptr = new_data_block_tmp.data + offset;
1103                 memcpy(ptr, sort_array[i].dentry, sort_array[i].rec_len);
1104
1105                 struct ext4_dir_entry_ll *tmp = ptr;
1106                 if (i < (idx - 1))
1107                         ext4_dir_entry_ll_set_entry_length(
1108                             tmp, sort_array[i].rec_len);
1109                 else
1110                         ext4_dir_entry_ll_set_entry_length(tmp,
1111                                                            block_size - offset);
1112
1113                 offset += sort_array[i].rec_len;
1114         }
1115
1116         block_size = ext4_sb_get_block_size(&inode_ref->fs->sb);
1117
1118         /* Do some steps to finish operation */
1119         sb = &inode_ref->fs->sb;
1120         if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM)) {
1121                 initialize_dir_tail(EXT4_DIRENT_TAIL(old_data_block->data,
1122                                         block_size));
1123                 initialize_dir_tail(EXT4_DIRENT_TAIL(new_data_block_tmp.data,
1124                                         block_size));
1125         }
1126         ext4_dir_set_checksum(inode_ref,
1127                         (struct ext4_dir_entry_ll *)old_data_block->data);
1128         ext4_dir_set_checksum(inode_ref,
1129                         (struct ext4_dir_entry_ll *)new_data_block_tmp.data);
1130         old_data_block->dirty = true;
1131         new_data_block_tmp.dirty = true;
1132
1133         free(sort_array);
1134         free(entry_buffer);
1135
1136         ext4_dir_dx_insert_entry(inode_ref,
1137                         index_block,
1138                         new_hash + continued, new_iblock);
1139
1140         *new_data_block = new_data_block_tmp;
1141
1142         return EOK;
1143 }
1144
1145 /**@brief  Split index node and maybe some parent nodes in the tree hierarchy.
1146  * @param inode_ref Directory i-node
1147  * @param dx_blocks Array with path from root to leaf node
1148  * @param dx_block  Leaf block to be split if needed
1149  * @return Error code
1150  */
1151 static int
1152 ext4_dir_dx_split_index(struct ext4_inode_ref *inode_ref,
1153                         struct ext4_dir_idx_block *dx_blocks,
1154                         struct ext4_dir_idx_block *dx_block,
1155                         struct ext4_dir_idx_block **new_dx_block)
1156 {
1157         struct ext4_sblock *sb = &inode_ref->fs->sb;
1158         struct ext4_dir_idx_entry *entries;
1159
1160         if (dx_block == dx_blocks)
1161                 entries =
1162                     ((struct ext4_dir_idx_root *)dx_block->block.data)
1163                         ->entries;
1164         else
1165                 entries =
1166                     ((struct ext4_dir_idx_node *)dx_block->block.data)
1167                         ->entries;
1168
1169         struct ext4_dir_idx_countlimit *countlimit =
1170             (struct ext4_dir_idx_countlimit *)entries;
1171
1172         uint16_t leaf_limit = ext4_dir_dx_countlimit_get_limit(countlimit);
1173         uint16_t leaf_count = ext4_dir_dx_countlimit_get_count(countlimit);
1174
1175         /* Check if is necessary to split index block */
1176         if (leaf_limit == leaf_count) {
1177                 size_t levels = dx_block - dx_blocks;
1178
1179                 struct ext4_dir_idx_entry *root_entries =
1180                     ((struct ext4_dir_idx_root *)dx_blocks[0].block.data)
1181                         ->entries;
1182
1183                 struct ext4_dir_idx_countlimit *root_countlimit =
1184                     (struct ext4_dir_idx_countlimit *)root_entries;
1185                 uint16_t root_limit =
1186                     ext4_dir_dx_countlimit_get_limit(root_countlimit);
1187                 uint16_t root_count =
1188                     ext4_dir_dx_countlimit_get_count(root_countlimit);
1189
1190                 /* Linux limitation */
1191                 if ((levels > 0) && (root_limit == root_count))
1192                         return ENOSPC;
1193
1194                 /* Add new block to directory */
1195                 ext4_fsblk_t new_fblock;
1196                 uint32_t new_iblock;
1197                 int rc = ext4_fs_append_inode_block(inode_ref, &new_fblock,
1198                                                     &new_iblock);
1199                 if (rc != EOK)
1200                         return rc;
1201
1202                 /* load new block */
1203                 struct ext4_block new_block;
1204                 rc =
1205                     ext4_block_get(inode_ref->fs->bdev, &new_block, new_fblock);
1206                 if (rc != EOK)
1207                         return rc;
1208
1209                 struct ext4_dir_idx_node *new_node =
1210                     (void *)new_block.data;
1211                 struct ext4_dir_idx_entry *new_entries = new_node->entries;
1212
1213                 memset(&new_node->fake, 0,
1214                        sizeof(struct ext4_fake_dir_entry));
1215
1216                 uint32_t block_size =
1217                     ext4_sb_get_block_size(&inode_ref->fs->sb);
1218
1219                 new_node->fake.entry_length = block_size;
1220
1221                 /* Split leaf node */
1222                 if (levels > 0) {
1223                         uint32_t count_left = leaf_count / 2;
1224                         uint32_t count_right = leaf_count - count_left;
1225                         uint32_t hash_right =
1226                             ext4_dir_dx_entry_get_hash(entries + count_left);
1227
1228                         /* Copy data to new node */
1229                         memcpy((void *)new_entries,
1230                                (void *)(entries + count_left),
1231                                count_right *
1232                                    sizeof(struct ext4_dir_idx_entry));
1233
1234                         /* Initialize new node */
1235                         struct ext4_dir_idx_countlimit *left_countlimit =
1236                             (struct ext4_dir_idx_countlimit *)entries;
1237                         struct ext4_dir_idx_countlimit *right_countlimit =
1238                             (struct ext4_dir_idx_countlimit *)new_entries;
1239
1240                         ext4_dir_dx_countlimit_set_count(left_countlimit,
1241                                                          count_left);
1242                         ext4_dir_dx_countlimit_set_count(right_countlimit,
1243                                                          count_right);
1244
1245                         uint32_t entry_space =
1246                             block_size -
1247                             sizeof(struct ext4_fake_dir_entry);
1248                         if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
1249                                 entry_space -= sizeof(struct ext4_dir_idx_tail);
1250
1251                         uint32_t node_limit =
1252                             entry_space / sizeof(struct ext4_dir_idx_entry);
1253
1254                         ext4_dir_dx_countlimit_set_limit(right_countlimit,
1255                                                          node_limit);
1256
1257                         /* Which index block is target for new entry */
1258                         uint32_t position_index =
1259                             (dx_block->position - dx_block->entries);
1260                         if (position_index >= count_left) {
1261                                 ext4_dir_set_dx_checksum(
1262                                                 inode_ref,
1263                                                 (struct ext4_dir_entry_ll *)
1264                                                 dx_block->block.data);
1265                                 dx_block->block.dirty = true;
1266
1267                                 struct ext4_block block_tmp = dx_block->block;
1268
1269                                 dx_block->block = new_block;
1270
1271                                 dx_block->position =
1272                                     new_entries + position_index - count_left;
1273                                 dx_block->entries = new_entries;
1274
1275                                 new_block = block_tmp;
1276                         }
1277
1278                         /* Finally insert new entry */
1279                         ext4_dir_dx_insert_entry(inode_ref,
1280                                                  dx_blocks, hash_right,
1281                                                  new_iblock);
1282                         ext4_dir_set_dx_checksum(inode_ref,
1283                                                 (struct ext4_dir_entry_ll *)
1284                                                         dx_blocks[0].block.data);
1285                         ext4_dir_set_dx_checksum(inode_ref,
1286                                                 (struct ext4_dir_entry_ll *)
1287                                                         dx_blocks[1].block.data);
1288                         dx_blocks[0].block.dirty = true;
1289                         dx_blocks[1].block.dirty = true;
1290
1291                         ext4_dir_set_dx_checksum(inode_ref,
1292                                                 (struct ext4_dir_entry_ll *)
1293                                                         new_block.data);
1294                         new_block.dirty = true;
1295                         return ext4_block_set(inode_ref->fs->bdev, &new_block);
1296                 } else {
1297                         /* Create second level index */
1298
1299                         /* Copy data from root to child block */
1300                         memcpy((void *)new_entries, (void *)entries,
1301                                leaf_count *
1302                                    sizeof(struct ext4_dir_idx_entry));
1303
1304                         struct ext4_dir_idx_countlimit *new_countlimit =
1305                             (struct ext4_dir_idx_countlimit *)new_entries;
1306
1307                         uint32_t entry_space =
1308                             block_size -
1309                             sizeof(struct ext4_fake_dir_entry);
1310                         if (ext4_sb_feature_ro_com(sb, EXT4_FRO_COM_METADATA_CSUM))
1311                                 entry_space -= sizeof(struct ext4_dir_idx_tail);
1312
1313                         uint32_t node_limit =
1314                             entry_space / sizeof(struct ext4_dir_idx_entry);
1315                         ext4_dir_dx_countlimit_set_limit(new_countlimit,
1316                                                          node_limit);
1317
1318                         /* Set values in root node */
1319                         struct ext4_dir_idx_countlimit
1320                             *new_root_countlimit =
1321                                 (struct ext4_dir_idx_countlimit *)entries;
1322
1323                         ext4_dir_dx_countlimit_set_count(new_root_countlimit,
1324                                                          1);
1325                         ext4_dir_dx_entry_set_block(entries, new_iblock);
1326
1327                         ((struct ext4_dir_idx_root *)dx_blocks[0]
1328                              .block.data)
1329                             ->info.indirect_levels = 1;
1330
1331                         /* Add new entry to the path */
1332                         dx_block = dx_blocks + 1;
1333                         dx_block->position =
1334                             dx_blocks->position - entries + new_entries;
1335                         dx_block->entries = new_entries;
1336                         dx_block->block = new_block;
1337
1338                         *new_dx_block = dx_block;
1339
1340                         ext4_dir_set_dx_checksum(inode_ref,
1341                                                 (struct ext4_dir_entry_ll *)
1342                                                         dx_blocks[0].block.data);
1343                         ext4_dir_set_dx_checksum(inode_ref,
1344                                                 (struct ext4_dir_entry_ll *)
1345                                                         dx_blocks[1].block.data);
1346                         dx_blocks[0].block.dirty = true;
1347                         dx_blocks[1].block.dirty = true;
1348                 }
1349         }
1350
1351         return EOK;
1352 }
1353
1354 int ext4_dir_dx_add_entry(struct ext4_inode_ref *parent,
1355                           struct ext4_inode_ref *child, const char *name)
1356 {
1357         int rc2 = EOK;
1358
1359         /* Get direct block 0 (index root) */
1360         ext4_fsblk_t root_block_addr;
1361         int rc =
1362             ext4_fs_get_inode_data_block_index(parent,
1363                             0, &root_block_addr,
1364                             false);
1365         if (rc != EOK)
1366                 return rc;
1367
1368         struct ext4_fs *fs = parent->fs;
1369         struct ext4_block root_block;
1370
1371         rc = ext4_block_get(fs->bdev, &root_block, root_block_addr);
1372         if (rc != EOK)
1373                 return rc;
1374
1375         if (!ext4_dir_dx_checksum_verify(parent,
1376                         (struct ext4_dir_entry_ll *)root_block.data)) {
1377                 ext4_dbg(DEBUG_DIR_IDX,
1378                          DBG_WARN "HTree root checksum failed."
1379                          "Inode: %" PRIu32", "
1380                          "Block: %" PRIu32"\n",
1381                          parent->index,
1382                          (uint32_t)0);
1383         }
1384
1385         /* Initialize hinfo structure (mainly compute hash) */
1386         uint32_t name_len = strlen(name);
1387         struct ext4_hash_info hinfo;
1388         rc = ext4_dir_hinfo_init(&hinfo, &root_block, &fs->sb, name_len, name);
1389         if (rc != EOK) {
1390                 ext4_block_set(fs->bdev, &root_block);
1391                 return EXT4_ERR_BAD_DX_DIR;
1392         }
1393
1394         /*
1395          * Hardcoded number 2 means maximum height of index
1396          * tree defined in Linux.
1397          */
1398         struct ext4_dir_idx_block dx_blocks[2];
1399         struct ext4_dir_idx_block *dx_block;
1400         struct ext4_dir_idx_block *dx_it;
1401
1402         rc = ext4_dir_dx_get_leaf(&hinfo, parent, &root_block, &dx_block,
1403                                   dx_blocks);
1404         if (rc != EOK) {
1405                 rc = EXT4_ERR_BAD_DX_DIR;
1406                 goto release_index;
1407         }
1408
1409         /* Try to insert to existing data block */
1410         uint32_t leaf_block_idx =
1411             ext4_dir_dx_entry_get_block(dx_block->position);
1412         ext4_fsblk_t leaf_block_addr;
1413         rc = ext4_fs_get_inode_data_block_index(parent, leaf_block_idx,
1414                                                 &leaf_block_addr,
1415                                                 false);
1416         if (rc != EOK)
1417                 goto release_index;
1418
1419         /*
1420          * Check if there is needed to split index node
1421          * (and recursively also parent nodes)
1422          */
1423         rc = ext4_dir_dx_split_index(parent, dx_blocks, dx_block, &dx_block);
1424         if (rc != EOK)
1425                 goto release_target_index;
1426
1427         struct ext4_block target_block;
1428         rc = ext4_block_get(fs->bdev, &target_block, leaf_block_addr);
1429         if (rc != EOK)
1430                 goto release_index;
1431
1432         if (!ext4_dir_checksum_verify(parent,
1433                         (struct ext4_dir_entry_ll *)target_block.data)) {
1434                 ext4_dbg(DEBUG_DIR_IDX,
1435                                 DBG_WARN "HTree leaf block checksum failed."
1436                                 "Inode: %" PRIu32", "
1437                                 "Block: %" PRIu32"\n",
1438                                 parent->index,
1439                                 leaf_block_idx);
1440         }
1441
1442         /* Check if insert operation passed */
1443         rc = ext4_dir_try_insert_entry(&fs->sb, parent, &target_block, child,
1444                                         name, name_len);
1445         if (rc == EOK)
1446                 goto release_target_index;
1447
1448         /* Split entries to two blocks (includes sorting by hash value) */
1449         struct ext4_block new_block;
1450         rc = ext4_dir_dx_split_data(parent, &hinfo, &target_block, dx_block,
1451                                     &new_block);
1452         if (rc != EOK) {
1453                 rc2 = rc;
1454                 goto release_target_index;
1455         }
1456
1457         /* Where to save new entry */
1458         uint32_t new_block_hash =
1459             ext4_dir_dx_entry_get_hash(dx_block->position + 1);
1460         if (hinfo.hash >= new_block_hash)
1461                 rc = ext4_dir_try_insert_entry(&fs->sb, parent, &new_block,
1462                                 child, name, name_len);
1463         else
1464                 rc = ext4_dir_try_insert_entry(&fs->sb, parent, &target_block,
1465                                 child, name, name_len);
1466
1467         /* Cleanup */
1468         rc = ext4_block_set(fs->bdev, &new_block);
1469         if (rc != EOK)
1470                 return rc;
1471
1472 /* Cleanup operations */
1473
1474 release_target_index:
1475         rc2 = rc;
1476
1477         rc = ext4_block_set(fs->bdev, &target_block);
1478         if (rc != EOK)
1479                 return rc;
1480
1481 release_index:
1482         if (rc != EOK)
1483                 rc2 = rc;
1484
1485         dx_it = dx_blocks;
1486
1487         while (dx_it <= dx_block) {
1488                 rc = ext4_block_set(fs->bdev, &dx_it->block);
1489                 if (rc != EOK)
1490                         return rc;
1491
1492                 dx_it++;
1493         }
1494
1495         return rc2;
1496 }
1497
1498 int ext4_dir_dx_reset_parent_inode(struct ext4_inode_ref *dir,
1499                                    uint32_t parent_inode)
1500 {
1501         /* Load block 0, where will be index root located */
1502         ext4_fsblk_t fblock;
1503         int rc = ext4_fs_get_inode_data_block_index(dir, 0, &fblock, false);
1504         if (rc != EOK)
1505                 return rc;
1506
1507         struct ext4_block block;
1508         rc = ext4_block_get(dir->fs->bdev, &block, fblock);
1509         if (rc != EOK)
1510                 return rc;
1511
1512         if (!ext4_dir_dx_checksum_verify(dir,
1513                         (struct ext4_dir_entry_ll *)block.data)) {
1514                 ext4_dbg(DEBUG_DIR_IDX,
1515                          DBG_WARN "HTree root checksum failed."
1516                          "Inode: %" PRIu32", "
1517                          "Block: %" PRIu32"\n",
1518                          dir->index,
1519                          (uint32_t)0);
1520         }
1521
1522         /* Initialize pointers to data structures */
1523         struct ext4_dir_idx_root *root = (void *)block.data;
1524
1525         /* Fill the inode field with a new parent ino. */
1526         ext4_dx_dot_entry_set_inode(&root->dots[1], parent_inode);
1527
1528         ext4_dir_set_dx_checksum(dir,
1529                                 (struct ext4_dir_entry_ll *)
1530                                         block.data);
1531         block.dirty = true;
1532
1533         return ext4_block_set(dir->fs->bdev, &block);
1534 }
1535
1536 /**
1537  * @}
1538  */