7ef875551c6ca6cc2842f6d74b0e263a411bb3eb
[lwext4.git] / lwext4 / ext4.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.h
34  * @brief Ext4 high level operations (file, directory, mountpoints...)
35  */
36
37 #include "ext4_config.h"
38 #include "ext4_blockdev.h"
39 #include "ext4_types.h"
40 #include "ext4_debug.h"
41 #include "ext4_errno.h"
42 #include "ext4_fs.h"
43 #include "ext4_dir.h"
44 #include "ext4_inode.h"
45 #include "ext4_super.h"
46 #include "ext4_dir_idx.h"
47 #include "ext4_xattr.h"
48 #include "ext4.h"
49
50 #include <stdlib.h>
51 #include <string.h>
52
53 /**@brief   Mount point OS dependent lock*/
54 #define EXT4_MP_LOCK(_m)                                                       \
55         do {                                                                   \
56                 if ((_m)->os_locks)                                            \
57                         (_m)->os_locks->lock();                                \
58         } while (0)
59
60 /**@brief   Mount point OS dependent unlock*/
61 #define EXT4_MP_UNLOCK(_m)                                                     \
62         do {                                                                   \
63                 if ((_m)->os_locks)                                            \
64                         (_m)->os_locks->unlock();                              \
65         } while (0)
66
67 /**@brief   Mount point descriptor.*/
68 struct ext4_mountpoint {
69
70         /**@brief   Mount done flag.*/
71         bool mounted;
72
73         /**@brief   Mount point name (@ref ext4_mount)*/
74         char name[32];
75
76         /**@brief   OS dependent lock/unlock functions.*/
77         const struct ext4_lock *os_locks;
78
79         /**@brief   Ext4 filesystem internals.*/
80         struct ext4_fs fs;
81
82         /**@brief   Dynamic allocation cache flag.*/
83         bool cache_dynamic;
84 };
85
86 /**@brief   Block devices descriptor.*/
87 struct _ext4_devices {
88
89         /**@brief   Block device name (@ref ext4_device_register)*/
90         char name[32];
91
92         /**@brief   Block device handle.*/
93         struct ext4_blockdev *bd;
94
95         /**@brief   Block cache handle.*/
96         struct ext4_bcache *bc;
97 };
98
99 /**@brief   Block devices.*/
100 struct _ext4_devices _bdevices[CONFIG_EXT4_BLOCKDEVS_COUNT];
101
102 /**@brief   Mountpoints.*/
103 struct ext4_mountpoint _mp[CONFIG_EXT4_MOUNTPOINTS_COUNT];
104
105 int ext4_device_register(struct ext4_blockdev *bd, struct ext4_bcache *bc,
106                          const char *dev_name)
107 {
108         uint32_t i;
109         ext4_assert(bd && dev_name);
110
111         for (i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) {
112                 if (!_bdevices[i].bd) {
113                         strcpy(_bdevices[i].name, dev_name);
114                         _bdevices[i].bd = bd;
115                         _bdevices[i].bc = bc;
116                         return EOK;
117                 }
118
119                 if (!strcmp(_bdevices[i].name, dev_name))
120                         return EOK;
121         }
122         return ENOSPC;
123 }
124
125 /****************************************************************************/
126
127 static bool ext4_is_dots(const uint8_t *name, size_t name_size)
128 {
129         if ((name_size == 1) && (name[0] == '.'))
130                 return true;
131
132         if ((name_size == 2) && (name[0] == '.') && (name[1] == '.'))
133                 return true;
134
135         return false;
136 }
137
138 static int ext4_has_children(bool *has_children, struct ext4_inode_ref *enode)
139 {
140         struct ext4_fs *fs = enode->fs;
141
142         /* Check if node is directory */
143         if (!ext4_inode_is_type(&fs->sb, enode->inode,
144                                 EXT4_INODE_MODE_DIRECTORY)) {
145                 *has_children = false;
146                 return EOK;
147         }
148
149         struct ext4_directory_iterator it;
150         int rc = ext4_dir_iterator_init(&it, enode, 0);
151         if (rc != EOK)
152                 return rc;
153
154         /* Find a non-empty directory entry */
155         bool found = false;
156         while (it.current != NULL) {
157                 if (ext4_dir_entry_ll_get_inode(it.current) != 0) {
158                         uint16_t name_size = ext4_dir_entry_ll_get_name_length(
159                             &fs->sb, it.current);
160                         if (!ext4_is_dots(it.current->name, name_size)) {
161                                 found = true;
162                                 break;
163                         }
164                 }
165
166                 rc = ext4_dir_iterator_next(&it);
167                 if (rc != EOK) {
168                         ext4_dir_iterator_fini(&it);
169                         return rc;
170                 }
171         }
172
173         rc = ext4_dir_iterator_fini(&it);
174         if (rc != EOK)
175                 return rc;
176
177         *has_children = found;
178
179         return EOK;
180 }
181
182 static int ext4_link(struct ext4_mountpoint *mp, struct ext4_inode_ref *parent,
183                      struct ext4_inode_ref *child, const char *name,
184                      uint32_t name_len, bool rename)
185 {
186         /* Check maximum name length */
187         if (name_len > EXT4_DIRECTORY_FILENAME_LEN)
188                 return EINVAL;
189
190         /* Add entry to parent directory */
191         int rc = ext4_dir_add_entry(parent, name, name_len, child);
192         if (rc != EOK)
193                 return rc;
194
195         /* Fill new dir -> add '.' and '..' entries.
196          * Also newly allocated inode should have 0 link count.
197          */
198         if (ext4_inode_is_type(&mp->fs.sb, child->inode,
199                                EXT4_INODE_MODE_DIRECTORY) &&
200             !rename) {
201                 rc = ext4_dir_add_entry(child, ".", strlen("."), child);
202                 if (rc != EOK) {
203                         ext4_dir_remove_entry(parent, name, strlen(name));
204                         return rc;
205                 }
206
207                 rc = ext4_dir_add_entry(child, "..", strlen(".."), parent);
208                 if (rc != EOK) {
209                         ext4_dir_remove_entry(parent, name, strlen(name));
210                         ext4_dir_remove_entry(child, ".", strlen("."));
211                         return rc;
212                 }
213
214                 /*New empty directory. Two links (. and ..) */
215                 ext4_inode_set_links_count(child->inode, 2);
216
217 #if CONFIG_DIR_INDEX_ENABLE
218                 /* Initialize directory index if supported */
219                 if (ext4_sb_has_feature_compatible(
220                         &mp->fs.sb, EXT4_FEATURE_COMPAT_DIR_INDEX)) {
221                         rc = ext4_dir_dx_init(child);
222                         if (rc != EOK)
223                                 return rc;
224
225                         ext4_inode_set_flag(child->inode,
226                                             EXT4_INODE_FLAG_INDEX);
227                         child->dirty = true;
228                 }
229 #endif
230
231                 ext4_fs_inode_links_count_inc(parent);
232                 child->dirty = true;
233                 parent->dirty = true;
234         } else {
235                 /*
236                  * In case we want to rename a directory,
237                  * we reset the original '..' pointer.
238                  */
239                 if (ext4_inode_is_type(&mp->fs.sb, child->inode,
240                                         EXT4_INODE_MODE_DIRECTORY)) {
241                         int has_flag_index =
242                                 ext4_inode_has_flag(child->inode,
243                                                     EXT4_INODE_FLAG_INDEX);
244                         struct ext4_directory_search_result result;
245                         if (!has_flag_index) {
246                                 rc = ext4_dir_find_entry(&result,
247                                                          child, "..",
248                                                          strlen(".."));
249                                 if (rc != EOK)
250                                         return EIO;
251
252                                 ext4_dir_entry_ll_set_inode(result.dentry,
253                                                             parent->index);
254                                 result.block.dirty = true;
255                                 rc = ext4_dir_destroy_result(child, &result);
256                                 if (rc != EOK)
257                                         return rc;
258
259                         } else {
260 #if CONFIG_DIR_INDEX_ENABLE
261                                 rc = ext4_dir_dx_reset_parent_inode(child,
262                                                 parent->index);
263                                 if (rc != EOK)
264                                         return rc;
265
266 #endif
267                         }
268
269                         ext4_fs_inode_links_count_inc(parent);
270                         parent->dirty = true;
271                 }
272                 if (!rename) {
273                         ext4_fs_inode_links_count_inc(child);
274                         child->dirty = true;
275                 }
276         }
277
278         return rc;
279 }
280
281 static int ext4_unlink(struct ext4_mountpoint *mp,
282                        struct ext4_inode_ref *parent,
283                        struct ext4_inode_ref *child_inode_ref, const char *name,
284                        uint32_t name_len)
285 {
286         bool has_children;
287         int rc = ext4_has_children(&has_children, child_inode_ref);
288         if (rc != EOK)
289                 return rc;
290
291         /* Cannot unlink non-empty node */
292         if (has_children)
293                 return ENOTEMPTY;
294
295         /* Remove entry from parent directory */
296         rc = ext4_dir_remove_entry(parent, name, name_len);
297         if (rc != EOK)
298                 return rc;
299
300         bool is_dir = ext4_inode_is_type(&mp->fs.sb, child_inode_ref->inode,
301                                          EXT4_INODE_MODE_DIRECTORY);
302
303         /* If directory - handle links from parent */
304         if (is_dir) {
305                 // ext4_assert(ext4_inode_get_links_count(child_inode_ref->inode)
306                 // == 1);
307                 ext4_fs_inode_links_count_dec(parent);
308                 parent->dirty = true;
309         }
310
311         /*
312          * TODO: Update timestamps of the parent
313          * (when we have wall-clock time).
314          *
315          * ext4_inode_set_change_inode_time(parent->inode, (uint32_t) now);
316          * ext4_inode_set_modification_time(parent->inode, (uint32_t) now);
317          * parent->dirty = true;
318          */
319
320         /*
321          * TODO: Update timestamp for inode.
322          *
323          * ext4_inode_set_change_inode_time(child_inode_ref->inode,
324          *     (uint32_t) now);
325          */
326         if (ext4_inode_get_links_count(child_inode_ref->inode)) {
327                 ext4_fs_inode_links_count_dec(child_inode_ref);
328                 child_inode_ref->dirty = true;
329         }
330
331         return EOK;
332 }
333
334 /****************************************************************************/
335
336 int ext4_mount(const char *dev_name, const char *mount_point)
337 {
338         ext4_assert(mount_point && dev_name);
339         int r;
340         int i;
341
342         uint32_t bsize;
343         struct ext4_blockdev *bd = 0;
344         struct ext4_bcache *bc = 0;
345         struct ext4_mountpoint *mp = 0;
346
347         if (mount_point[strlen(mount_point) - 1] != '/')
348                 return ENOTSUP;
349
350         for (i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) {
351                 if (_bdevices[i].name) {
352                         if (!strcmp(dev_name, _bdevices[i].name)) {
353                                 bd = _bdevices[i].bd;
354                                 bc = _bdevices[i].bc;
355                                 break;
356                         }
357                 }
358         }
359
360         if (!bd)
361                 return ENODEV;
362
363         for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
364                 if (!_mp[i].mounted) {
365                         strcpy(_mp[i].name, mount_point);
366                         _mp[i].mounted = 1;
367                         mp = &_mp[i];
368                         break;
369                 }
370
371                 if (!strcmp(_mp[i].name, mount_point))
372                         return EOK;
373         }
374
375         if (!mp)
376                 return ENOMEM;
377
378         r = ext4_block_init(bd);
379         if (r != EOK)
380                 return r;
381
382         r = ext4_fs_init(&mp->fs, bd);
383         if (r != EOK) {
384                 ext4_block_fini(bd);
385                 return r;
386         }
387
388         bsize = ext4_sb_get_block_size(&mp->fs.sb);
389         ext4_block_set_lb_size(bd, bsize);
390
391         mp->cache_dynamic = 0;
392
393         if (!bc) {
394                 /*Automatic block cache alloc.*/
395                 mp->cache_dynamic = 1;
396                 bc = malloc(sizeof(struct ext4_bcache));
397
398                 r = ext4_bcache_init_dynamic(bc, CONFIG_BLOCK_DEV_CACHE_SIZE,
399                                              bsize);
400                 if (r != EOK) {
401                         free(bc);
402                         ext4_block_fini(bd);
403                         return r;
404                 }
405         }
406
407         if (bsize != bc->itemsize)
408                 return ENOTSUP;
409
410         /*Bind block cache to block device*/
411         r = ext4_block_bind_bcache(bd, bc);
412         if (r != EOK) {
413                 ext4_block_fini(bd);
414                 if (mp->cache_dynamic) {
415                         ext4_bcache_fini_dynamic(bc);
416                         free(bc);
417                 }
418                 return r;
419         }
420
421         return r;
422 }
423
424 int ext4_umount(const char *mount_point)
425 {
426         int i;
427         int r;
428         struct ext4_mountpoint *mp = 0;
429
430         for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
431                 if (!strcmp(_mp[i].name, mount_point)) {
432                         mp = &_mp[i];
433                         break;
434                 }
435         }
436
437         if (!mp)
438                 return ENODEV;
439
440         r = ext4_fs_fini(&mp->fs);
441         if (r != EOK)
442                 return r;
443
444         mp->mounted = 0;
445
446         if (mp->cache_dynamic) {
447                 ext4_bcache_fini_dynamic(mp->fs.bdev->bc);
448                 free(mp->fs.bdev->bc);
449         }
450
451         return ext4_block_fini(mp->fs.bdev);
452 }
453
454 static struct ext4_mountpoint *ext4_get_mount(const char *path)
455 {
456         int i;
457         for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
458
459                 if (!_mp[i].mounted)
460                         continue;
461
462                 if (!strncmp(_mp[i].name, path, strlen(_mp[i].name)))
463                         return &_mp[i];
464         }
465         return 0;
466 }
467
468 int ext4_mount_point_stats(const char *mount_point,
469                            struct ext4_mount_stats *stats)
470 {
471         struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
472
473         if (!mp)
474                 return ENOENT;
475
476         EXT4_MP_LOCK(mp);
477         stats->inodes_count = ext4_get32(&mp->fs.sb, inodes_count);
478         stats->free_inodes_count = ext4_get32(&mp->fs.sb, free_inodes_count);
479         stats->blocks_count = ext4_sb_get_blocks_cnt(&mp->fs.sb);
480         stats->free_blocks_count = ext4_sb_get_free_blocks_cnt(&mp->fs.sb);
481         stats->block_size = ext4_sb_get_block_size(&mp->fs.sb);
482
483         stats->block_group_count = ext4_block_group_cnt(&mp->fs.sb);
484         stats->blocks_per_group = ext4_get32(&mp->fs.sb, blocks_per_group);
485         stats->inodes_per_group = ext4_get32(&mp->fs.sb, inodes_per_group);
486
487         memcpy(stats->volume_name, mp->fs.sb.volume_name, 16);
488         EXT4_MP_UNLOCK(mp);
489
490         return EOK;
491 }
492
493 int ext4_mount_setup_locks(const char *mount_point,
494                            const struct ext4_lock *locks)
495 {
496         uint32_t i;
497         struct ext4_mountpoint *mp = 0;
498
499         for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
500                 if (!strcmp(_mp[i].name, mount_point)) {
501                         mp = &_mp[i];
502                         break;
503                 }
504         }
505         if (!mp)
506                 return ENOENT;
507
508         mp->os_locks = locks;
509         return EOK;
510 }
511
512 /********************************FILE OPERATIONS*****************************/
513
514 static int ext4_path_check(const char *path, bool *is_goal)
515 {
516         int i;
517
518         for (i = 0; i < EXT4_DIRECTORY_FILENAME_LEN; ++i) {
519
520                 if (path[i] == '/') {
521                         *is_goal = false;
522                         return i;
523                 }
524
525                 if (path[i] == 0) {
526                         *is_goal = true;
527                         return i;
528                 }
529         }
530
531         return 0;
532 }
533
534 static bool ext4_parse_flags(const char *flags, uint32_t *file_flags)
535 {
536         if (!flags)
537                 return false;
538
539         if (!strcmp(flags, "r") || !strcmp(flags, "rb")) {
540                 *file_flags = O_RDONLY;
541                 return true;
542         }
543
544         if (!strcmp(flags, "w") || !strcmp(flags, "wb")) {
545                 *file_flags = O_WRONLY | O_CREAT | O_TRUNC;
546                 return true;
547         }
548
549         if (!strcmp(flags, "a") || !strcmp(flags, "ab")) {
550                 *file_flags = O_WRONLY | O_CREAT | O_APPEND;
551                 return true;
552         }
553
554         if (!strcmp(flags, "r+") || !strcmp(flags, "rb+") ||
555             !strcmp(flags, "r+b")) {
556                 *file_flags = O_RDWR;
557                 return true;
558         }
559
560         if (!strcmp(flags, "w+") || !strcmp(flags, "wb+") ||
561             !strcmp(flags, "w+b")) {
562                 *file_flags = O_RDWR | O_CREAT | O_TRUNC;
563                 return true;
564         }
565
566         if (!strcmp(flags, "a+") || !strcmp(flags, "ab+") ||
567             !strcmp(flags, "a+b")) {
568                 *file_flags = O_RDWR | O_CREAT | O_APPEND;
569                 return true;
570         }
571
572         return false;
573 }
574
575 /*
576  * NOTICE: if filetype is equal to EXT4_DIRENTRY_UNKNOWN,
577  * any filetype of the target dir entry will be accepted.
578  */
579 static int ext4_generic_open2(ext4_file *f, const char *path, int flags,
580                               int filetype, uint32_t *parent_inode,
581                               uint32_t *name_off)
582 {
583         bool is_goal = false;
584         uint8_t inode_type = EXT4_DIRENTRY_UNKNOWN;
585         uint32_t next_inode;
586
587         int r;
588         struct ext4_mountpoint *mp = ext4_get_mount(path);
589         struct ext4_directory_search_result result;
590         struct ext4_inode_ref ref;
591
592         f->mp = 0;
593
594         if (!mp)
595                 return ENOENT;
596
597         f->flags = flags;
598
599         /*Skip mount point*/
600         path += strlen(mp->name);
601
602         if (name_off)
603                 *name_off = strlen(mp->name);
604
605         /*Load root*/
606         r = ext4_fs_get_inode_ref(&mp->fs, EXT4_INODE_ROOT_INDEX, &ref);
607
608         if (r != EOK)
609                 return r;
610
611         if (parent_inode)
612                 *parent_inode = ref.index;
613
614         int len = ext4_path_check(path, &is_goal);
615
616         while (1) {
617
618                 len = ext4_path_check(path, &is_goal);
619
620                 if (!len) {
621                         /*If root open was request.*/
622                         if (is_goal &&
623                             ((filetype == EXT4_DIRENTRY_DIR) ||
624                              (filetype == EXT4_DIRENTRY_UNKNOWN)))
625                                 break;
626
627                         r = ENOENT;
628                         break;
629                 }
630
631                 r = ext4_dir_find_entry(&result, &ref, path, len);
632                 if (r != EOK) {
633
634                         /*Destroy last result*/
635                         ext4_dir_destroy_result(&ref, &result);
636
637                         if (r != ENOENT)
638                                 break;
639
640                         if (!(f->flags & O_CREAT))
641                                 break;
642
643                         /*O_CREAT allows create new entry*/
644                         struct ext4_inode_ref child_ref;
645                         r = ext4_fs_alloc_inode(
646                             &mp->fs, &child_ref,
647                             is_goal ? filetype
648                                     : EXT4_DIRENTRY_DIR);
649                         if (r != EOK)
650                                 break;
651
652
653                         /*Link with root dir.*/
654                         r = ext4_link(mp, &ref, &child_ref, path, len, false);
655                         if (r != EOK) {
656                                 /*Fail. Free new inode.*/
657                                 ext4_fs_free_inode(&child_ref);
658                                 /*We do not want to write new inode.
659                                   But block has to be released.*/
660                                 child_ref.dirty = false;
661                                 ext4_fs_put_inode_ref(&child_ref);
662                                 break;
663                         }
664
665                         ext4_fs_put_inode_ref(&child_ref);
666
667                         continue;
668                 }
669
670                 if (parent_inode)
671                         *parent_inode = ref.index;
672
673                 next_inode = ext4_dir_entry_ll_get_inode(result.dentry);
674                 inode_type =
675                     ext4_dir_entry_ll_get_inode_type(&mp->fs.sb, result.dentry);
676
677                 r = ext4_dir_destroy_result(&ref, &result);
678                 if (r != EOK)
679                         break;
680
681                 /*If expected file error*/
682                 if (inode_type != EXT4_DIRENTRY_DIR && !is_goal) {
683                         r = ENOENT;
684                         break;
685                 }
686                 if (filetype != EXT4_DIRENTRY_UNKNOWN) {
687                         if ((inode_type != filetype) && is_goal) {
688                                 r = ENOENT;
689                                 break;
690                         }
691                 }
692
693                 r = ext4_fs_put_inode_ref(&ref);
694                 if (r != EOK)
695                         break;
696
697                 r = ext4_fs_get_inode_ref(&mp->fs, next_inode, &ref);
698                 if (r != EOK)
699                         break;
700
701                 if (is_goal)
702                         break;
703
704                 path += len + 1;
705
706                 if (name_off)
707                         *name_off += len + 1;
708         };
709
710         if (r != EOK) {
711                 ext4_fs_put_inode_ref(&ref);
712                 return r;
713         }
714
715         if (is_goal) {
716
717                 if ((f->flags & O_TRUNC) &&
718                     (inode_type == EXT4_DIRENTRY_REG_FILE)) {
719
720                         r = ext4_fs_truncate_inode(&ref, 0);
721                         if (r != EOK) {
722                                 ext4_fs_put_inode_ref(&ref);
723                                 return r;
724                         }
725                 }
726
727                 f->mp = mp;
728                 f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);
729                 f->inode = ref.index;
730                 f->fpos = 0;
731
732                 if (f->flags & O_APPEND)
733                         f->fpos = f->fsize;
734
735         }
736
737         r = ext4_fs_put_inode_ref(&ref);
738         return r;
739 }
740
741 /****************************************************************************/
742
743 static int ext4_generic_open(ext4_file *f, const char *path, const char *flags,
744                              bool file_expect, uint32_t *parent_inode,
745                              uint32_t *name_off)
746 {
747         uint32_t iflags;
748         int filetype;
749         if (ext4_parse_flags(flags, &iflags) == false)
750                 return EINVAL;
751
752         if (file_expect == true)
753                 filetype = EXT4_DIRENTRY_REG_FILE;
754         else
755                 filetype = EXT4_DIRENTRY_DIR;
756
757         return ext4_generic_open2(f, path, iflags, filetype, parent_inode,
758                                   name_off);
759 }
760
761 static int __ext4_create_hardlink(const char *path,
762                 struct ext4_inode_ref *child_ref,
763                 bool rename)
764 {
765         bool is_goal = false;
766         uint8_t inode_type = EXT4_DIRENTRY_DIR;
767         uint32_t next_inode;
768
769         int r;
770         struct ext4_mountpoint *mp = ext4_get_mount(path);
771         struct ext4_directory_search_result result;
772         struct ext4_inode_ref ref;
773
774         if (!mp)
775                 return ENOENT;
776
777         /*Skip mount point*/
778         path += strlen(mp->name);
779
780         /*Load root*/
781         r = ext4_fs_get_inode_ref(&mp->fs, EXT4_INODE_ROOT_INDEX, &ref);
782
783         if (r != EOK)
784                 return r;
785
786         int len = ext4_path_check(path, &is_goal);
787
788         while (1) {
789
790                 len = ext4_path_check(path, &is_goal);
791
792                 if (!len) {
793                         /*If root open was request.*/
794                         if (is_goal)
795                                 r = EINVAL;
796                         else
797                                 r = ENOENT;
798                         break;
799                 }
800
801                 r = ext4_dir_find_entry(&result, &ref, path, len);
802                 if (r != EOK) {
803
804                         /*Destroy last result*/
805                         ext4_dir_destroy_result(&ref, &result);
806
807                         if (r != ENOENT || !is_goal)
808                                 break;
809
810                         /*Link with root dir.*/
811                         r = ext4_link(mp, &ref, child_ref, path, len, rename);
812                         break;
813                 } else if (r == EOK && is_goal) {
814                         /*Destroy last result*/
815                         ext4_dir_destroy_result(&ref, &result);
816                         r = EEXIST;
817                         break;
818                 }
819
820                 next_inode = result.dentry->inode;
821                 inode_type =
822                         ext4_dir_entry_ll_get_inode_type(&mp->fs.sb, result.dentry);
823
824                 r = ext4_dir_destroy_result(&ref, &result);
825                 if (r != EOK)
826                         break;
827
828                 if (inode_type == EXT4_DIRENTRY_REG_FILE) {
829                         if (is_goal)
830                                 r = EEXIST;
831                         else
832                                 r = ENOENT;
833
834                         break;
835                 }
836
837                 r = ext4_fs_put_inode_ref(&ref);
838                 if (r != EOK)
839                         break;
840
841                 r = ext4_fs_get_inode_ref(&mp->fs, next_inode, &ref);
842                 if (r != EOK)
843                         break;
844
845                 if (is_goal)
846                         break;
847
848                 path += len + 1;
849         };
850
851         if (r != EOK) {
852                 ext4_fs_put_inode_ref(&ref);
853                 return r;
854         }
855
856         r = ext4_fs_put_inode_ref(&ref);
857         return r;
858 }
859
860 static int ext4_remove_orig_reference(const char *path,
861                                       uint32_t name_off,
862                                       struct ext4_inode_ref *parent_ref,
863                                       struct ext4_inode_ref *child_ref)
864 {
865         bool is_goal;
866         int r;
867         int len;
868         struct ext4_mountpoint *mp = ext4_get_mount(path);
869
870         if (!mp)
871                 return ENOENT;
872
873         /*Set path*/
874         path += name_off;
875
876         len = ext4_path_check(path, &is_goal);
877
878         /* Remove entry from parent directory */
879         r = ext4_dir_remove_entry(parent_ref, path, len);
880         if (r != EOK)
881                 goto Finish;
882
883         if (ext4_inode_is_type(&mp->fs.sb, child_ref->inode,
884                                EXT4_INODE_MODE_DIRECTORY)) {
885                 ext4_fs_inode_links_count_dec(parent_ref);
886                 parent_ref->dirty = true;
887         }
888 Finish:
889         return r;
890 }
891
892 int ext4_flink(const char *path, const char *hardlink_path)
893 {
894         int r;
895         ext4_file f;
896         uint32_t name_off;
897         bool child_loaded = false;
898         uint32_t parent_inode, child_inode;
899         struct ext4_mountpoint *mp = ext4_get_mount(path);
900         struct ext4_mountpoint *target_mp = ext4_get_mount(hardlink_path);
901         struct ext4_inode_ref child_ref;
902
903         if (!mp)
904                 return ENOENT;
905
906         /* Will that happen? Anyway return EINVAL for such case. */
907         if (mp != target_mp)
908                 return EINVAL;
909
910         EXT4_MP_LOCK(mp);
911
912         r = ext4_generic_open2(&f, path, O_RDONLY,
913                                EXT4_DIRENTRY_UNKNOWN,
914                                &parent_inode, &name_off);
915         if (r != EOK)
916                 goto Finish;
917
918         child_inode = f.inode;
919         ext4_fclose(&f);
920
921         /*We have file to unlink. Load it.*/
922         r = ext4_fs_get_inode_ref(&mp->fs, child_inode, &child_ref);
923         if (r != EOK)
924                 goto Finish;
925
926         child_loaded = true;
927
928         /* Creating hardlink for directory is not allowed. */
929         if (ext4_inode_is_type(&mp->fs.sb, child_ref.inode,
930                                EXT4_INODE_MODE_DIRECTORY)) {
931                 r = EINVAL;
932                 goto Finish;
933         }
934
935         r = __ext4_create_hardlink(hardlink_path, &child_ref, false);
936
937 Finish:
938         if (child_loaded)
939                 ext4_fs_put_inode_ref(&child_ref);
940
941         EXT4_MP_UNLOCK(mp);
942         return r;
943
944 }
945
946 int ext4_frename(const char *path, const char *new_path)
947 {
948         int r;
949         ext4_file f;
950         uint32_t name_off;
951         bool parent_loaded = false, child_loaded = false;
952         uint32_t parent_inode, child_inode;
953         struct ext4_mountpoint *mp = ext4_get_mount(path);
954         struct ext4_inode_ref child_ref, parent_ref;
955
956         if (!mp)
957                 return ENOENT;
958
959         EXT4_MP_LOCK(mp);
960
961         r = ext4_generic_open2(&f, path, O_RDONLY,
962                         EXT4_DIRENTRY_UNKNOWN,
963                         &parent_inode, &name_off);
964         if (r != EOK)
965                 goto Finish;
966
967         child_inode = f.inode;
968         ext4_fclose(&f);
969
970         /*Load parent*/
971         r = ext4_fs_get_inode_ref(&mp->fs, parent_inode, &parent_ref);
972         if (r != EOK)
973                 goto Finish;
974
975         parent_loaded = true;
976
977         /*We have file to unlink. Load it.*/
978         r = ext4_fs_get_inode_ref(&mp->fs, child_inode, &child_ref);
979         if (r != EOK)
980                 goto Finish;
981
982         child_loaded = true;
983
984         r = __ext4_create_hardlink(new_path, &child_ref, true);
985         if (r != EOK)
986                 goto Finish;
987
988         r = ext4_remove_orig_reference(path, name_off,
989                                        &parent_ref, &child_ref);
990         if (r != EOK)
991                 goto Finish;
992
993 Finish:
994         if (parent_loaded)
995                 ext4_fs_put_inode_ref(&parent_ref);
996
997         if (child_loaded)
998                 ext4_fs_put_inode_ref(&child_ref);
999
1000         EXT4_MP_UNLOCK(mp);
1001         return r;
1002
1003 }
1004
1005 /****************************************************************************/
1006
1007 int ext4_get_sblock(const char *mount_point, struct ext4_sblock **sb)
1008 {
1009         struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
1010
1011         if (!mp)
1012                 return ENOENT;
1013
1014         *sb = &mp->fs.sb;
1015         return EOK;
1016 }
1017
1018 int ext4_cache_write_back(const char *path, bool on)
1019 {
1020         struct ext4_mountpoint *mp = ext4_get_mount(path);
1021
1022         if (!mp)
1023                 return ENOENT;
1024
1025         EXT4_MP_LOCK(mp);
1026         ext4_block_cache_write_back(mp->fs.bdev, on);
1027         EXT4_MP_UNLOCK(mp);
1028         return EOK;
1029 }
1030
1031 int ext4_fremove(const char *path)
1032 {
1033         ext4_file f;
1034         uint32_t parent_inode;
1035         uint32_t name_off;
1036         bool is_goal;
1037         int r;
1038         int len;
1039         struct ext4_inode_ref child;
1040         struct ext4_inode_ref parent;
1041         struct ext4_mountpoint *mp = ext4_get_mount(path);
1042
1043         if (!mp)
1044                 return ENOENT;
1045
1046         EXT4_MP_LOCK(mp);
1047         r = ext4_generic_open2(&f, path, O_RDWR,
1048                                EXT4_DIRENTRY_UNKNOWN,
1049                                &parent_inode, &name_off);
1050         if (r != EOK) {
1051                 EXT4_MP_UNLOCK(mp);
1052                 return r;
1053         }
1054
1055         /*Load parent*/
1056         r = ext4_fs_get_inode_ref(&mp->fs, parent_inode, &parent);
1057         if (r != EOK) {
1058                 EXT4_MP_UNLOCK(mp);
1059                 return r;
1060         }
1061
1062         /*We have file to delete. Load it.*/
1063         r = ext4_fs_get_inode_ref(&mp->fs, f.inode, &child);
1064         if (r != EOK) {
1065                 ext4_fs_put_inode_ref(&parent);
1066                 EXT4_MP_UNLOCK(mp);
1067                 return r;
1068         }
1069
1070         /*Set path*/
1071         path += name_off;
1072
1073         len = ext4_path_check(path, &is_goal);
1074
1075         /*Unlink from parent*/
1076         r = ext4_unlink(mp, &parent, &child, path, len);
1077         if (r != EOK)
1078                 goto Finish;
1079
1080         /*Link count is zero, the inode should be freed. */
1081         if (!ext4_inode_get_links_count(child.inode)) {
1082                 ext4_inode_set_deletion_time(child.inode, 0xFFFFFFFF);
1083                 /*Turncate*/
1084                 ext4_block_cache_write_back(mp->fs.bdev, 1);
1085                 /*Truncate may be IO heavy. Do it writeback cache mode.*/
1086                 r = ext4_fs_truncate_inode(&child, 0);
1087                 ext4_block_cache_write_back(mp->fs.bdev, 0);
1088
1089                 if (r != EOK)
1090                         goto Finish;
1091
1092                 r = ext4_fs_free_inode(&child);
1093                 if (r != EOK)
1094                         goto Finish;
1095         }
1096
1097 Finish:
1098         ext4_fs_put_inode_ref(&child);
1099         ext4_fs_put_inode_ref(&parent);
1100         EXT4_MP_UNLOCK(mp);
1101         return r;
1102 }
1103
1104 int ext4_fill_raw_inode(const char *path,
1105                         uint32_t *ret_ino,
1106                         struct ext4_inode *inode)
1107 {
1108         int r;
1109         ext4_file f;
1110         struct ext4_inode_ref inode_ref;
1111         struct ext4_mountpoint *mp = ext4_get_mount(path);
1112         uint32_t ino;
1113
1114         if (!mp)
1115                 return ENOENT;
1116
1117         EXT4_MP_LOCK(mp);
1118
1119         r = ext4_generic_open2(&f, path, O_RDONLY,
1120                                EXT4_DIRENTRY_UNKNOWN,
1121                                NULL, NULL);
1122         if (r != EOK) {
1123                 EXT4_MP_UNLOCK(mp);
1124                 return r;
1125         }
1126
1127         ino = f.inode;
1128         ext4_fclose(&f);
1129
1130         /*Load parent*/
1131         r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
1132         if (r != EOK) {
1133                 EXT4_MP_UNLOCK(mp);
1134                 return r;
1135         }
1136
1137         memcpy(inode, inode_ref.inode, sizeof(struct ext4_inode));
1138
1139         ext4_fs_put_inode_ref(&inode_ref);
1140         EXT4_MP_UNLOCK(mp);
1141
1142         if (ret_ino)
1143                 *ret_ino = ino;
1144
1145         return r;
1146 }
1147
1148 int ext4_fopen(ext4_file *f, const char *path, const char *flags)
1149 {
1150         struct ext4_mountpoint *mp = ext4_get_mount(path);
1151         int r;
1152
1153         if (!mp)
1154                 return ENOENT;
1155
1156         EXT4_MP_LOCK(mp);
1157         ext4_block_cache_write_back(mp->fs.bdev, 1);
1158         r = ext4_generic_open(f, path, flags, true, 0, 0);
1159         ext4_block_cache_write_back(mp->fs.bdev, 0);
1160         EXT4_MP_UNLOCK(mp);
1161         return r;
1162 }
1163
1164 int ext4_fopen2(ext4_file *f, const char *path, int flags)
1165 {
1166         struct ext4_mountpoint *mp = ext4_get_mount(path);
1167         int r;
1168         int filetype;
1169
1170         if (!mp)
1171                 return ENOENT;
1172
1173         filetype = EXT4_DIRENTRY_REG_FILE;
1174
1175         EXT4_MP_LOCK(mp);
1176         ext4_block_cache_write_back(mp->fs.bdev, 1);
1177         r = ext4_generic_open2(f, path, flags, filetype, 0, 0);
1178         ext4_block_cache_write_back(mp->fs.bdev, 0);
1179         EXT4_MP_UNLOCK(mp);
1180         return r;
1181 }
1182
1183 int ext4_fclose(ext4_file *f)
1184 {
1185         ext4_assert(f && f->mp);
1186
1187         f->mp = 0;
1188         f->flags = 0;
1189         f->inode = 0;
1190         f->fpos = f->fsize = 0;
1191
1192         return EOK;
1193 }
1194
1195 static int ext4_ftruncate_no_lock(ext4_file *f, uint64_t size)
1196 {
1197         struct ext4_inode_ref ref;
1198         int r;
1199
1200
1201         r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
1202         if (r != EOK) {
1203                 EXT4_MP_UNLOCK(f->mp);
1204                 return r;
1205         }
1206
1207         /*Sync file size*/
1208         f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);
1209         if (f->fsize <= size) {
1210                 r = EOK;
1211                 goto Finish;
1212         }
1213
1214         /*Start write back cache mode.*/
1215         r = ext4_block_cache_write_back(f->mp->fs.bdev, 1);
1216         if (r != EOK)
1217                 goto Finish;
1218
1219         r = ext4_fs_truncate_inode(&ref, size);
1220         if (r != EOK)
1221                 goto Finish;
1222
1223         f->fsize = size;
1224         if (f->fpos > size)
1225                 f->fpos = size;
1226
1227         /*Stop write back cache mode*/
1228         ext4_block_cache_write_back(f->mp->fs.bdev, 0);
1229
1230         if (r != EOK)
1231                 goto Finish;
1232
1233 Finish:
1234         ext4_fs_put_inode_ref(&ref);
1235         return r;
1236
1237 }
1238
1239 int ext4_ftruncate(ext4_file *f, uint64_t size)
1240 {
1241         int r;
1242         ext4_assert(f && f->mp);
1243
1244         if (f->flags & O_RDONLY)
1245                 return EPERM;
1246
1247         EXT4_MP_LOCK(f->mp);
1248
1249         r = ext4_ftruncate_no_lock(f, size);
1250
1251         EXT4_MP_UNLOCK(f->mp);
1252         return r;
1253 }
1254
1255 int ext4_fread(ext4_file *f, void *buf, size_t size, size_t *rcnt)
1256 {
1257         uint32_t u;
1258         ext4_fsblk_t fblock;
1259         uint32_t fblock_cnt;
1260         uint32_t sblock;
1261         uint32_t block_size;
1262         uint8_t *u8_buf = buf;
1263         int r;
1264         struct ext4_block b;
1265         struct ext4_inode_ref ref;
1266
1267         ext4_assert(f && f->mp);
1268
1269         if (f->flags & O_WRONLY)
1270                 return EPERM;
1271
1272         if (!size)
1273                 return EOK;
1274
1275         EXT4_MP_LOCK(f->mp);
1276
1277         if (rcnt)
1278                 *rcnt = 0;
1279
1280         r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
1281         if (r != EOK) {
1282                 EXT4_MP_UNLOCK(f->mp);
1283                 return r;
1284         }
1285
1286         /*Sync file size*/
1287         f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);
1288
1289         block_size = ext4_sb_get_block_size(&f->mp->fs.sb);
1290         size = size > (f->fsize - f->fpos) ? (f->fsize - f->fpos) : size;
1291         sblock = (f->fpos) / block_size;
1292         u = (f->fpos) % block_size;
1293
1294         /*If the size of symlink is smaller than 60 bytes*/
1295         if (ext4_inode_is_type(&f->mp->fs.sb, ref.inode, EXT4_INODE_MODE_SOFTLINK)
1296                         && f->fsize < sizeof(ref.inode->blocks)
1297                         && !ext4_inode_get_blocks_count(&f->mp->fs.sb, ref.inode)) {
1298                 char *content = (char *)ref.inode->blocks;
1299                 if (f->fpos < f->fsize) {
1300                         r = (u + size > f->fsize)
1301                                 ?(f->fsize - u)
1302                                 :(size);
1303                         memcpy(buf, content + u, r);
1304                         if (rcnt)
1305                                 *rcnt = r;
1306
1307                 } else {
1308                         r = 0;
1309                         if (rcnt)
1310                                 *rcnt = 0;
1311
1312                 }
1313
1314                 r = EOK;
1315                 goto Finish;
1316         }
1317
1318         if (u) {
1319
1320                 uint32_t ll = size > (block_size - u) ? (block_size - u) : size;
1321
1322                 r = ext4_fs_get_inode_data_block_index(&ref,
1323                                 sblock, &fblock,
1324                                 true);
1325                 if (r != EOK)
1326                         goto Finish;
1327
1328                 /* Do we get an unwritten range? */
1329                 if (fblock != 0) {
1330                         r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
1331                         if (r != EOK)
1332                                 goto Finish;
1333
1334                         memcpy(u8_buf, b.data + u, ll);
1335
1336                         r = ext4_block_set(f->mp->fs.bdev, &b);
1337                         if (r != EOK)
1338                                 goto Finish;
1339                 } else {
1340                         /* Yes, we do. */
1341                         memset(u8_buf, 0, ll);
1342                 }
1343
1344                 u8_buf += ll;
1345                 size -= ll;
1346                 f->fpos += ll;
1347
1348                 if (rcnt)
1349                         *rcnt += ll;
1350
1351                 sblock++;
1352         }
1353
1354         fblock_cnt = 0;
1355         while (size >= block_size) {
1356
1357                 r = ext4_fs_get_inode_data_block_index(&ref, sblock,
1358                                                        &fblock,
1359                                                        true);
1360                 if (r != EOK)
1361                         goto Finish;
1362
1363                 sblock++;
1364                 fblock_cnt = 1;
1365
1366                 r = ext4_blocks_get_direct(f->mp->fs.bdev, u8_buf, fblock,
1367                                            fblock_cnt);
1368                 if (r != EOK)
1369                         goto Finish;
1370
1371                 size -= block_size * fblock_cnt;
1372                 u8_buf += block_size * fblock_cnt;
1373                 f->fpos += block_size * fblock_cnt;
1374
1375                 if (rcnt)
1376                         *rcnt += block_size * fblock_cnt;
1377
1378         }
1379
1380         if (size) {
1381                 r = ext4_fs_get_inode_data_block_index(&ref,
1382                                                 sblock, &fblock,
1383                                                 true);
1384                 if (r != EOK)
1385                         goto Finish;
1386
1387                 r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
1388                 if (r != EOK)
1389                         goto Finish;
1390
1391                 memcpy(u8_buf, b.data, size);
1392
1393                 r = ext4_block_set(f->mp->fs.bdev, &b);
1394                 if (r != EOK)
1395                         goto Finish;
1396
1397                 f->fpos += size;
1398
1399                 if (rcnt)
1400                         *rcnt += size;
1401         }
1402
1403 Finish:
1404         ext4_fs_put_inode_ref(&ref);
1405         EXT4_MP_UNLOCK(f->mp);
1406         return r;
1407 }
1408
1409 int ext4_fwrite(ext4_file *f, const void *buf, size_t size, size_t *wcnt)
1410 {
1411         uint32_t u;
1412         ext4_fsblk_t fblock;
1413
1414         uint32_t sblock;
1415         uint32_t file_blocks;
1416         uint32_t block_size;
1417         uint32_t fblock_cnt;
1418
1419         struct ext4_block b;
1420         struct ext4_inode_ref ref;
1421         const uint8_t *u8_buf = buf;
1422         int r;
1423
1424         ext4_assert(f && f->mp);
1425
1426         if (f->flags & O_RDONLY)
1427                 return EPERM;
1428
1429         if (!size)
1430                 return EOK;
1431
1432         EXT4_MP_LOCK(f->mp);
1433
1434         if (wcnt)
1435                 *wcnt = 0;
1436
1437         r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
1438         if (r != EOK) {
1439                 EXT4_MP_UNLOCK(f->mp);
1440                 return r;
1441         }
1442
1443         /*Sync file size*/
1444         f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);
1445
1446         block_size = ext4_sb_get_block_size(&f->mp->fs.sb);
1447
1448         file_blocks = (f->fsize + block_size - 1) / block_size;
1449
1450         sblock = (f->fpos) / block_size;
1451
1452         u = (f->fpos) % block_size;
1453
1454         if (u) {
1455                 uint32_t ll = size > (block_size - u) ? (block_size - u) : size;
1456
1457                 r = ext4_fs_init_inode_data_block_index(&ref, sblock, &fblock);
1458                 if (r != EOK)
1459                         goto Finish;
1460
1461                 r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
1462                 if (r != EOK)
1463                         goto Finish;
1464
1465                 memcpy(b.data + u, u8_buf, ll);
1466                 b.dirty = true;
1467
1468                 r = ext4_block_set(f->mp->fs.bdev, &b);
1469                 if (r != EOK)
1470                         goto Finish;
1471
1472                 u8_buf += ll;
1473                 size -= ll;
1474                 f->fpos += ll;
1475
1476                 if (wcnt)
1477                         *wcnt += ll;
1478
1479                 sblock++;
1480         }
1481
1482         /*Start write back cache mode.*/
1483         r = ext4_block_cache_write_back(f->mp->fs.bdev, 1);
1484         if (r != EOK)
1485                 goto Finish;
1486
1487         fblock_cnt = 0;
1488         while (size >= block_size) {
1489
1490                 if (sblock < file_blocks) {
1491                         r = ext4_fs_init_inode_data_block_index(
1492                             &ref, sblock, &fblock);
1493                         if (r != EOK)
1494                                 break;
1495                 } else {
1496                         r = ext4_fs_append_inode_block(&ref, &fblock,
1497                                                        &sblock);
1498                         if (r != EOK)
1499                                 break;
1500                 }
1501
1502                 sblock++;
1503                 fblock_cnt = 1;
1504
1505                 r = ext4_blocks_set_direct(f->mp->fs.bdev, u8_buf, fblock,
1506                                            fblock_cnt);
1507                 if (r != EOK)
1508                         break;
1509
1510                 size -= block_size * fblock_cnt;
1511                 u8_buf += block_size * fblock_cnt;
1512                 f->fpos += block_size * fblock_cnt;
1513
1514                 if (wcnt)
1515                         *wcnt += block_size * fblock_cnt;
1516
1517         }
1518
1519         /*Stop write back cache mode*/
1520         ext4_block_cache_write_back(f->mp->fs.bdev, 0);
1521
1522         if (r != EOK)
1523                 goto Finish;
1524
1525         if (size) {
1526                 if (sblock < file_blocks) {
1527                         r = ext4_fs_init_inode_data_block_index(&ref, sblock,
1528                                                                &fblock);
1529                         if (r != EOK)
1530                                 goto Finish;
1531                 } else {
1532                         r = ext4_fs_append_inode_block(&ref, &fblock, &sblock);
1533                         if (r != EOK)
1534                                 goto Finish;
1535                 }
1536
1537                 r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
1538                 if (r != EOK)
1539                         goto Finish;
1540
1541                 memcpy(b.data, u8_buf, size);
1542                 b.dirty = true;
1543
1544                 r = ext4_block_set(f->mp->fs.bdev, &b);
1545                 if (r != EOK)
1546                         goto Finish;
1547
1548                 f->fpos += size;
1549
1550                 if (wcnt)
1551                         *wcnt += size;
1552         }
1553
1554         if (f->fpos > f->fsize) {
1555                 f->fsize = f->fpos;
1556                 ext4_inode_set_size(ref.inode, f->fsize);
1557                 ref.dirty = true;
1558         }
1559
1560 Finish:
1561         ext4_fs_put_inode_ref(&ref);
1562         EXT4_MP_UNLOCK(f->mp);
1563         return r;
1564 }
1565
1566 int ext4_fseek(ext4_file *f, uint64_t offset, uint32_t origin)
1567 {
1568         switch (origin) {
1569         case SEEK_SET:
1570                 if (offset > f->fsize)
1571                         return EINVAL;
1572
1573                 f->fpos = offset;
1574                 return EOK;
1575         case SEEK_CUR:
1576                 if ((offset + f->fpos) > f->fsize)
1577                         return EINVAL;
1578
1579                 f->fpos += offset;
1580                 return EOK;
1581         case SEEK_END:
1582                 if (offset > f->fsize)
1583                         return EINVAL;
1584
1585                 f->fpos = f->fsize - offset;
1586                 return EOK;
1587         }
1588         return EINVAL;
1589 }
1590
1591 uint64_t ext4_ftell(ext4_file *f) { return f->fpos; }
1592
1593 uint64_t ext4_fsize(ext4_file *f) { return f->fsize; }
1594
1595 int ext4_chmod(const char *path, uint32_t mode)
1596 {
1597         int r;
1598         uint32_t ino;
1599         ext4_file f;
1600         struct ext4_sblock *sb;
1601         struct ext4_inode_ref inode_ref;
1602         struct ext4_mountpoint *mp = ext4_get_mount(path);
1603
1604         if (!mp)
1605                 return ENOENT;
1606
1607         EXT4_MP_LOCK(mp);
1608
1609         r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DIRENTRY_UNKNOWN, 0, 0);
1610         if (r != EOK) {
1611                 EXT4_MP_UNLOCK(mp);
1612                 return r;
1613         }
1614         ino = f.inode;
1615         sb = &mp->fs.sb;
1616         ext4_fclose(&f);
1617         r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
1618         if (r != EOK) {
1619                 EXT4_MP_UNLOCK(mp);
1620                 return r;
1621         }
1622
1623         ext4_inode_set_mode(sb, inode_ref.inode, mode);
1624         inode_ref.dirty = true;
1625
1626         ext4_fs_put_inode_ref(&inode_ref);
1627         EXT4_MP_UNLOCK(mp);
1628         return r;
1629 }
1630
1631 int ext4_chown(const char *path, uint32_t uid, uint32_t gid)
1632 {
1633         int r;
1634         ext4_file f;
1635         uint32_t ino;
1636         struct ext4_inode_ref inode_ref;
1637         struct ext4_mountpoint *mp = ext4_get_mount(path);
1638
1639         if (!mp)
1640                 return ENOENT;
1641
1642         EXT4_MP_LOCK(mp);
1643
1644         r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DIRENTRY_UNKNOWN, 0, 0);
1645         if (r != EOK) {
1646                 EXT4_MP_UNLOCK(mp);
1647                 return r;
1648         }
1649         ino = f.inode;
1650         ext4_fclose(&f);
1651         r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
1652         if (r != EOK) {
1653                 EXT4_MP_UNLOCK(mp);
1654                 return r;
1655         }
1656
1657         ext4_inode_set_uid(inode_ref.inode, uid);
1658         ext4_inode_set_gid(inode_ref.inode, gid);
1659         inode_ref.dirty = true;
1660
1661         ext4_fs_put_inode_ref(&inode_ref);
1662         EXT4_MP_UNLOCK(mp);
1663         return r;
1664 }
1665
1666 int ext4_file_set_atime(const char *path, uint32_t atime)
1667 {
1668         int r;
1669         ext4_file f;
1670         uint32_t ino;
1671         struct ext4_inode_ref inode_ref;
1672         struct ext4_mountpoint *mp = ext4_get_mount(path);
1673
1674         if (!mp)
1675                 return ENOENT;
1676
1677         EXT4_MP_LOCK(mp);
1678
1679         r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DIRENTRY_UNKNOWN, 0, 0);
1680         if (r != EOK) {
1681                 EXT4_MP_UNLOCK(mp);
1682                 return r;
1683         }
1684         ino = f.inode;
1685         ext4_fclose(&f);
1686         r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
1687         if (r != EOK) {
1688                 EXT4_MP_UNLOCK(mp);
1689                 return r;
1690         }
1691
1692         ext4_inode_set_access_time(inode_ref.inode, atime);
1693         inode_ref.dirty = true;
1694
1695         ext4_fs_put_inode_ref(&inode_ref);
1696         EXT4_MP_UNLOCK(mp);
1697         return r;
1698 }
1699
1700 int ext4_file_set_mtime(const char *path, uint32_t mtime)
1701 {
1702         int r;
1703         ext4_file f;
1704         uint32_t ino;
1705         struct ext4_inode_ref inode_ref;
1706         struct ext4_mountpoint *mp = ext4_get_mount(path);
1707
1708         if (!mp)
1709                 return ENOENT;
1710
1711         EXT4_MP_LOCK(mp);
1712
1713         r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DIRENTRY_UNKNOWN, 0, 0);
1714         if (r != EOK) {
1715                 EXT4_MP_UNLOCK(mp);
1716                 return r;
1717         }
1718         ino = f.inode;
1719         ext4_fclose(&f);
1720         r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
1721         if (r != EOK) {
1722                 EXT4_MP_UNLOCK(mp);
1723                 return r;
1724         }
1725
1726         ext4_inode_set_modification_time(inode_ref.inode, mtime);
1727         inode_ref.dirty = true;
1728
1729         ext4_fs_put_inode_ref(&inode_ref);
1730         EXT4_MP_UNLOCK(mp);
1731         return r;
1732 }
1733
1734 int ext4_file_set_ctime(const char *path, uint32_t ctime)
1735 {
1736         int r;
1737         ext4_file f;
1738         uint32_t ino;
1739         struct ext4_inode_ref inode_ref;
1740         struct ext4_mountpoint *mp = ext4_get_mount(path);
1741
1742         if (!mp)
1743                 return ENOENT;
1744
1745         EXT4_MP_LOCK(mp);
1746
1747         r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DIRENTRY_UNKNOWN, 0, 0);
1748         if (r != EOK) {
1749                 EXT4_MP_UNLOCK(mp);
1750                 return r;
1751         }
1752         ino = f.inode;
1753         ext4_fclose(&f);
1754         r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
1755         if (r != EOK) {
1756                 EXT4_MP_UNLOCK(mp);
1757                 return r;
1758         }
1759
1760         ext4_inode_set_change_inode_time(inode_ref.inode, ctime);
1761         inode_ref.dirty = true;
1762
1763         ext4_fs_put_inode_ref(&inode_ref);
1764         EXT4_MP_UNLOCK(mp);
1765         return r;
1766 }
1767
1768 static int ext4_fsymlink_set(ext4_file *f, const void *buf, uint32_t size)
1769 {
1770         struct ext4_block b;
1771         struct ext4_inode_ref ref;
1772         uint32_t sblock;
1773         ext4_fsblk_t fblock;
1774         uint32_t block_size;
1775         int r;
1776
1777         ext4_assert(f && f->mp);
1778
1779         if (!size)
1780                 return EOK;
1781
1782         r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
1783         if (r != EOK) {
1784                 EXT4_MP_UNLOCK(f->mp);
1785                 return r;
1786         }
1787
1788         /*Sync file size*/
1789         block_size = ext4_sb_get_block_size(&f->mp->fs.sb);
1790         if (size > block_size) {
1791                 r = EINVAL;
1792                 goto Finish;
1793         }
1794         r = ext4_ftruncate_no_lock(f, 0);
1795         if (r != EOK)
1796                 goto Finish;
1797
1798         /*Start write back cache mode.*/
1799         r = ext4_block_cache_write_back(f->mp->fs.bdev, 1);
1800         if (r != EOK)
1801                 goto Finish;
1802
1803         /*If the size of symlink is smaller than 60 bytes*/
1804         if (size < sizeof(ref.inode->blocks)) {
1805                 char *content = (char *)ref.inode->blocks;
1806                 memset(content, 0, sizeof(ref.inode->blocks));
1807                 memcpy(content, buf, size);
1808                 ext4_inode_clear_flag(ref.inode, EXT4_INODE_FLAG_EXTENTS);
1809         } else {
1810                 ext4_fs_inode_blocks_init(&f->mp->fs, &ref);
1811                 r = ext4_fs_append_inode_block(&ref, &fblock, &sblock);
1812                 if (r != EOK)
1813                         goto Finish;
1814
1815                 r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
1816                 if (r != EOK)
1817                         goto Finish;
1818
1819                 memcpy(b.data, buf, size);
1820                 b.dirty = true;
1821                 r = ext4_block_set(f->mp->fs.bdev, &b);
1822                 if (r != EOK)
1823                         goto Finish;
1824         }
1825
1826         /*Stop write back cache mode*/
1827         ext4_block_cache_write_back(f->mp->fs.bdev, 0);
1828
1829         if (r != EOK)
1830                 goto Finish;
1831
1832         ext4_inode_set_size(ref.inode, size);
1833         ref.dirty = true;
1834
1835         f->fsize = size;
1836         if (f->fpos > size)
1837                 f->fpos = size;
1838
1839 Finish:
1840         ext4_fs_put_inode_ref(&ref);
1841         return r;
1842 }
1843
1844 int ext4_fsymlink(const char *target, const char *path)
1845 {
1846         struct ext4_mountpoint *mp = ext4_get_mount(path);
1847         int r;
1848         ext4_file f;
1849         int filetype;
1850
1851         if (!mp)
1852                 return ENOENT;
1853
1854         filetype = EXT4_DIRENTRY_SYMLINK;
1855
1856         EXT4_MP_LOCK(mp);
1857         ext4_block_cache_write_back(mp->fs.bdev, 1);
1858         r = ext4_generic_open2(&f, path, O_RDWR|O_CREAT, filetype, 0, 0);
1859         if (r == EOK)
1860                 r = ext4_fsymlink_set(&f, target, strlen(target));
1861         else
1862                 goto Finish;
1863
1864         ext4_fclose(&f);
1865
1866 Finish:
1867         ext4_block_cache_write_back(mp->fs.bdev, 0);
1868         EXT4_MP_UNLOCK(mp);
1869         return r;
1870 }
1871
1872 int ext4_readlink(const char *path, char *buf, size_t bufsize, size_t *rcnt)
1873 {
1874         struct ext4_mountpoint *mp = ext4_get_mount(path);
1875         int r;
1876         ext4_file f;
1877         int filetype;
1878
1879         if (!mp)
1880                 return ENOENT;
1881
1882         if (!buf)
1883                 return EINVAL;
1884
1885         memset(buf, 0, sizeof(bufsize));
1886
1887         filetype = EXT4_DIRENTRY_SYMLINK;
1888
1889         EXT4_MP_LOCK(mp);
1890         ext4_block_cache_write_back(mp->fs.bdev, 1);
1891         r = ext4_generic_open2(&f, path, O_RDONLY, filetype, 0, 0);
1892         if (r == EOK)
1893                 r = ext4_fread(&f, buf, bufsize, rcnt);
1894         else
1895                 goto Finish;
1896
1897         ext4_fclose(&f);
1898
1899 Finish:
1900         ext4_block_cache_write_back(mp->fs.bdev, 0);
1901         EXT4_MP_UNLOCK(mp);
1902         return r;
1903 }
1904
1905 int ext4_setxattr(const char *path, const char *name, size_t name_len,
1906                   const void *data, size_t data_size, bool replace)
1907 {
1908         int r = EOK;
1909         ext4_file f;
1910         uint32_t inode;
1911         uint8_t name_index;
1912         const char *dissected_name = NULL;
1913         size_t dissected_len = 0;
1914         struct ext4_xattr_ref xattr_ref;
1915         struct ext4_inode_ref inode_ref;
1916         struct ext4_mountpoint *mp = ext4_get_mount(path);
1917         if (!mp)
1918                 return ENOENT;
1919
1920         dissected_name = ext4_extract_xattr_name(name, name_len,
1921                                 &name_index, &dissected_len);
1922         if (!dissected_len)
1923                 return EINVAL;
1924
1925         EXT4_MP_LOCK(mp);
1926         r = ext4_generic_open2(&f, path, O_RDWR,
1927                                 EXT4_DIRENTRY_UNKNOWN, 0, 0);
1928         if (r != EOK)
1929                 goto Finish;
1930         inode = f.inode;
1931         ext4_fclose(&f);
1932
1933         r = ext4_fs_get_inode_ref(&mp->fs, inode, &inode_ref);
1934         if (r != EOK)
1935                 goto Finish;
1936
1937         r = ext4_fs_get_xattr_ref(&mp->fs, &inode_ref,
1938                               &xattr_ref);
1939         if (r != EOK) {
1940                 ext4_fs_put_inode_ref(&inode_ref);
1941                 goto Finish;
1942         }
1943
1944         r = ext4_fs_set_xattr(&xattr_ref, name_index, dissected_name,
1945                         dissected_len, data, data_size, replace);
1946
1947         ext4_fs_put_xattr_ref(&xattr_ref);
1948         ext4_fs_put_inode_ref(&inode_ref);
1949 Finish:
1950         EXT4_MP_UNLOCK(mp);
1951         return r;
1952 }
1953
1954 int ext4_getxattr(const char *path, const char *name, size_t name_len,
1955                   void *buf, size_t buf_size, size_t *data_size)
1956 {
1957         int r = EOK;
1958         ext4_file f;
1959         uint32_t inode;
1960         uint8_t name_index;
1961         const char *dissected_name = NULL;
1962         size_t dissected_len = 0;
1963         struct ext4_xattr_ref xattr_ref;
1964         struct ext4_inode_ref inode_ref;
1965         struct ext4_mountpoint *mp = ext4_get_mount(path);
1966         if (!mp)
1967                 return ENOENT;
1968
1969         dissected_name = ext4_extract_xattr_name(name, name_len,
1970                                 &name_index, &dissected_len);
1971         if (!dissected_len)
1972                 return EINVAL;
1973
1974         EXT4_MP_LOCK(mp);
1975         r = ext4_generic_open2(&f, path, O_RDWR,
1976                                 EXT4_DIRENTRY_UNKNOWN, 0, 0);
1977         if (r != EOK)
1978                 goto Finish;
1979         inode = f.inode;
1980         ext4_fclose(&f);
1981
1982         r = ext4_fs_get_inode_ref(&mp->fs, inode, &inode_ref);
1983         if (r != EOK)
1984                 goto Finish;
1985
1986         r = ext4_fs_get_xattr_ref(&mp->fs, &inode_ref,
1987                               &xattr_ref);
1988         if (r != EOK) {
1989                 ext4_fs_put_inode_ref(&inode_ref);
1990                 goto Finish;
1991         }
1992
1993         r = ext4_fs_get_xattr(&xattr_ref, name_index,
1994                                 dissected_name, dissected_len,
1995                                 buf, buf_size, data_size);
1996
1997         ext4_fs_put_xattr_ref(&xattr_ref);
1998         ext4_fs_put_inode_ref(&inode_ref);
1999 Finish:
2000         EXT4_MP_UNLOCK(mp);
2001         return r;
2002 }
2003
2004 struct ext4_listxattr_iterator {
2005         char *list;
2006         char *list_ptr;
2007         size_t size;
2008         size_t ret_size;
2009         bool list_too_small;
2010         bool get_required_size;
2011 };
2012
2013 static int ext4_iterate_ea_list(struct ext4_xattr_ref *ref,
2014                                 struct ext4_xattr_item *item)
2015 {
2016         struct ext4_listxattr_iterator *lxi;
2017         lxi = ref->iter_arg;
2018         if (!lxi->get_required_size) {
2019                 size_t prefix_len;
2020                 const char *prefix;
2021                 prefix = ext4_get_xattr_name_prefix(item->name_index,
2022                                         &prefix_len);
2023                 if (lxi->ret_size +
2024                         prefix_len + item->name_len + 1 >
2025                         lxi->size) {
2026
2027                         lxi->list_too_small = 1;
2028                         return EXT4_XATTR_ITERATE_STOP;
2029                 }
2030                 if (prefix) {
2031                         memcpy(lxi->list_ptr, prefix, prefix_len);
2032                         lxi->list_ptr += prefix_len;
2033                         lxi->ret_size += prefix_len;
2034                 }
2035                 memcpy(lxi->list_ptr, item->name, item->name_len);
2036                 lxi->list_ptr[item->name_len] = 0;
2037                 lxi->list_ptr += item->name_len + 1;
2038         }
2039         lxi->ret_size += item->name_len + 1;
2040         return EXT4_XATTR_ITERATE_CONT;
2041 }
2042
2043 int ext4_listxattr(const char *path, char *list, size_t size, size_t *ret_size)
2044 {
2045         int r = EOK;
2046         ext4_file f;
2047         uint32_t inode;
2048         struct ext4_xattr_ref xattr_ref;
2049         struct ext4_inode_ref inode_ref;
2050         struct ext4_listxattr_iterator lxi;
2051         struct ext4_mountpoint *mp = ext4_get_mount(path);
2052         if (!mp)
2053                 return ENOENT;
2054
2055         lxi.list = list;
2056         lxi.list_ptr = list;
2057         lxi.size = size;
2058         lxi.ret_size = 0;
2059         lxi.list_too_small = false;
2060         lxi.get_required_size = (!size) ? true : false;
2061
2062         EXT4_MP_LOCK(mp);
2063         r = ext4_generic_open2(&f, path, O_RDWR,
2064                                 EXT4_DIRENTRY_UNKNOWN, 0, 0);
2065         if (r != EOK)
2066                 goto Finish;
2067         inode = f.inode;
2068         ext4_fclose(&f);
2069
2070         r = ext4_fs_get_inode_ref(&mp->fs, inode, &inode_ref);
2071         if (r != EOK)
2072                 goto Finish;
2073
2074         r = ext4_fs_get_xattr_ref(&mp->fs, &inode_ref,
2075                               &xattr_ref);
2076         if (r != EOK) {
2077                 ext4_fs_put_inode_ref(&inode_ref);
2078                 goto Finish;
2079         }
2080
2081         xattr_ref.iter_arg = &lxi;
2082         ext4_fs_xattr_iterate(&xattr_ref, ext4_iterate_ea_list);
2083         if (lxi.list_too_small)
2084                 r = ERANGE;
2085
2086         if (r == EOK) {
2087                 if (ret_size)
2088                         *ret_size = lxi.ret_size;
2089
2090         }
2091         ext4_fs_put_xattr_ref(&xattr_ref);
2092         ext4_fs_put_inode_ref(&inode_ref);
2093 Finish:
2094         EXT4_MP_UNLOCK(mp);
2095         return r;
2096
2097 }
2098
2099 int ext4_removexattr(const char *path, const char *name, size_t name_len)
2100 {
2101         int r = EOK;
2102         ext4_file f;
2103         uint32_t inode;
2104         uint8_t name_index;
2105         const char *dissected_name = NULL;
2106         size_t dissected_len = 0;
2107         struct ext4_xattr_ref xattr_ref;
2108         struct ext4_inode_ref inode_ref;
2109         struct ext4_mountpoint *mp = ext4_get_mount(path);
2110         if (!mp)
2111                 return ENOENT;
2112
2113         dissected_name = ext4_extract_xattr_name(name, name_len,
2114                                 &name_index, &dissected_len);
2115         if (!dissected_len)
2116                 return EINVAL;
2117
2118         EXT4_MP_LOCK(mp);
2119         r = ext4_generic_open2(&f, path, O_RDWR,
2120                                 EXT4_DIRENTRY_UNKNOWN, 0, 0);
2121         if (r != EOK)
2122                 goto Finish;
2123         inode = f.inode;
2124         ext4_fclose(&f);
2125
2126         r = ext4_fs_get_inode_ref(&mp->fs, inode, &inode_ref);
2127         if (r != EOK)
2128                 goto Finish;
2129
2130         r = ext4_fs_get_xattr_ref(&mp->fs, &inode_ref,
2131                               &xattr_ref);
2132         if (r != EOK) {
2133                 ext4_fs_put_inode_ref(&inode_ref);
2134                 goto Finish;
2135         }
2136
2137         r = ext4_fs_remove_xattr(&xattr_ref, name_index, dissected_name,
2138                                 dissected_len);
2139
2140         ext4_fs_put_xattr_ref(&xattr_ref);
2141         ext4_fs_put_inode_ref(&inode_ref);
2142 Finish:
2143         EXT4_MP_UNLOCK(mp);
2144         return r;
2145
2146 }
2147
2148 /*********************************DIRECTORY OPERATION************************/
2149
2150 int ext4_dir_rm(const char *path)
2151 {
2152         int r;
2153         int len;
2154         ext4_file f;
2155
2156         struct ext4_mountpoint *mp = ext4_get_mount(path);
2157         struct ext4_inode_ref current;
2158         struct ext4_inode_ref child;
2159         struct ext4_directory_iterator it;
2160
2161         uint32_t name_off;
2162         uint32_t inode_up;
2163         uint32_t inode_current;
2164         uint32_t depth = 1;
2165
2166         bool has_children;
2167         bool is_goal;
2168         bool dir_end;
2169
2170         if (!mp)
2171                 return ENOENT;
2172
2173         EXT4_MP_LOCK(mp);
2174
2175         /*Check if exist.*/
2176         r = ext4_generic_open(&f, path, "r", false, &inode_up, &name_off);
2177         if (r != EOK) {
2178                 EXT4_MP_UNLOCK(mp);
2179                 return r;
2180         }
2181
2182         path += name_off;
2183         len = ext4_path_check(path, &is_goal);
2184
2185         inode_current = f.inode;
2186         dir_end = false;
2187
2188         ext4_block_cache_write_back(mp->fs.bdev, 1);
2189
2190         do {
2191                 /*Load directory node.*/
2192                 r = ext4_fs_get_inode_ref(&f.mp->fs, inode_current, &current);
2193                 if (r != EOK) {
2194                         break;
2195                 }
2196
2197                 /*Initialize iterator.*/
2198                 r = ext4_dir_iterator_init(&it, &current, 0);
2199                 if (r != EOK) {
2200                         ext4_fs_put_inode_ref(&current);
2201                         break;
2202                 }
2203
2204                 while (r == EOK) {
2205
2206                         if (!it.current) {
2207                                 dir_end = true;
2208                                 break;
2209                         }
2210
2211                         /*Get up directory inode when ".." entry*/
2212                         if ((it.current->name_length == 2) &&
2213                             ext4_is_dots(it.current->name,
2214                                          it.current->name_length)) {
2215                                 inode_up = ext4_dir_entry_ll_get_inode(it.current);
2216                         }
2217
2218                         /*If directory or file entry,  but not "." ".." entry*/
2219                         if (!ext4_is_dots(it.current->name,
2220                                           it.current->name_length)) {
2221
2222                                 /*Get child inode reference do unlink
2223                                  * directory/file.*/
2224                                 r = ext4_fs_get_inode_ref(&f.mp->fs,
2225                                         ext4_dir_entry_ll_get_inode(it.current),
2226                                         &child);
2227                                 if (r != EOK)
2228                                         break;
2229
2230                                 /*If directory with no leaf children*/
2231                                 r = ext4_has_children(&has_children, &child);
2232                                 if (r != EOK) {
2233                                         ext4_fs_put_inode_ref(&child);
2234                                         break;
2235                                 }
2236
2237                                 if (has_children) {
2238                                         /*Has directory children. Go into this
2239                                          * directory.*/
2240                                         inode_up = inode_current;
2241                                         inode_current = ext4_dir_entry_ll_get_inode(it.current);
2242                                         depth++;
2243                                         ext4_fs_put_inode_ref(&child);
2244                                         break;
2245                                 }
2246
2247                                 /*No children in child directory or file. Just
2248                                  * unlink.*/
2249                                 r = ext4_unlink(f.mp, &current, &child,
2250                                                 (char *)it.current->name,
2251                                                 it.current->name_length);
2252                                 if (r != EOK) {
2253                                         ext4_fs_put_inode_ref(&child);
2254                                         break;
2255                                 }
2256
2257                                 ext4_inode_set_deletion_time(child.inode,
2258                                                              0xFFFFFFFF);
2259                                 ext4_inode_set_links_count(child.inode, 0);
2260                                 child.dirty = true;
2261                                 /*Turncate*/
2262                                 r = ext4_fs_truncate_inode(&child, 0);
2263                                 if (r != EOK) {
2264                                         ext4_fs_put_inode_ref(&child);
2265                                         break;
2266                                 }
2267
2268                                 r = ext4_fs_free_inode(&child);
2269                                 if (r != EOK) {
2270                                         ext4_fs_put_inode_ref(&child);
2271                                         break;
2272                                 }
2273
2274                                 r = ext4_fs_put_inode_ref(&child);
2275                                 if (r != EOK)
2276                                         break;
2277                         }
2278
2279                         r = ext4_dir_iterator_next(&it);
2280                 }
2281
2282                 if (dir_end) {
2283                         /*Directory iterator reached last entry*/
2284                         ext4_has_children(&has_children, &current);
2285                         if (!has_children) {
2286                                 inode_current = inode_up;
2287                                 if (depth)
2288                                         depth--;
2289                         }
2290                         /*Last unlink*/
2291                         if (!depth) {
2292                                 /*Load parent.*/
2293                                 struct ext4_inode_ref parent;
2294                                 r = ext4_fs_get_inode_ref(&f.mp->fs, inode_up,
2295                                                           &parent);
2296                                 if (r != EOK)
2297                                         goto End;
2298
2299                                 /* In this place all directories should be
2300                                  * unlinked.
2301                                  * Last unlink from root of current directory*/
2302                                 r = ext4_unlink(f.mp, &parent, &current,
2303                                                 (char *)path, len);
2304                                 if (r != EOK) {
2305                                         ext4_fs_put_inode_ref(&parent);
2306                                         goto End;
2307                                 }
2308
2309                                 if (ext4_inode_get_links_count(current.inode) ==
2310                                     2) {
2311                                         ext4_inode_set_deletion_time(
2312                                             current.inode, 0xFFFFFFFF);
2313                                         ext4_inode_set_links_count(
2314                                             current.inode, 0);
2315                                         current.dirty = true;
2316                                         /*Turncate*/
2317                                         r = ext4_fs_truncate_inode(&current, 0);
2318                                         if (r != EOK) {
2319                                                 ext4_fs_put_inode_ref(&parent);
2320                                                 goto End;
2321                                         }
2322
2323                                         r = ext4_fs_free_inode(&current);
2324                                         if (r != EOK) {
2325                                                 ext4_fs_put_inode_ref(&parent);
2326                                                 goto End;
2327                                         }
2328                                 }
2329
2330                                 r = ext4_fs_put_inode_ref(&parent);
2331                                 if (r != EOK)
2332                                         goto End;
2333                         }
2334                 }
2335
2336         End:
2337                 ext4_dir_iterator_fini(&it);
2338                 ext4_fs_put_inode_ref(&current);
2339                 dir_end = false;
2340
2341                 /*When something goes wrong. End loop.*/
2342                 if (r != EOK)
2343                         break;
2344
2345         } while (depth);
2346
2347         ext4_block_cache_write_back(mp->fs.bdev, 0);
2348         EXT4_MP_UNLOCK(mp);
2349         return r;
2350 }
2351
2352 int ext4_dir_mk(const char *path)
2353 {
2354         int r;
2355         ext4_file f;
2356
2357         struct ext4_mountpoint *mp = ext4_get_mount(path);
2358
2359         if (!mp)
2360                 return ENOENT;
2361
2362         EXT4_MP_LOCK(mp);
2363
2364         /*Check if exist.*/
2365         r = ext4_generic_open(&f, path, "r", false, 0, 0);
2366         if (r == EOK) {
2367                 /*Directory already created*/
2368                 EXT4_MP_UNLOCK(mp);
2369                 return r;
2370         }
2371
2372         /*Create new dir*/
2373         r = ext4_generic_open(&f, path, "w", false, 0, 0);
2374         if (r != EOK) {
2375                 EXT4_MP_UNLOCK(mp);
2376                 return r;
2377         }
2378
2379         EXT4_MP_UNLOCK(mp);
2380         return r;
2381 }
2382
2383 int ext4_dir_open(ext4_dir *d, const char *path)
2384 {
2385         struct ext4_mountpoint *mp = ext4_get_mount(path);
2386         int r;
2387
2388         if (!mp)
2389                 return ENOENT;
2390
2391         EXT4_MP_LOCK(mp);
2392         r = ext4_generic_open(&d->f, path, "r", false, 0, 0);
2393         d->next_off = 0;
2394         EXT4_MP_UNLOCK(mp);
2395         return r;
2396 }
2397
2398 int ext4_dir_close(ext4_dir *d)
2399 {
2400     return ext4_fclose(&d->f);
2401 }
2402
2403 const ext4_direntry *ext4_dir_entry_next(ext4_dir *d)
2404 {
2405 #define EXT4_DIR_ENTRY_OFFSET_TERM (uint64_t)(-1)
2406
2407         int r;
2408         ext4_direntry *de = 0;
2409         struct ext4_inode_ref dir;
2410         struct ext4_directory_iterator it;
2411
2412         EXT4_MP_LOCK(d->f.mp);
2413
2414         if (d->next_off == EXT4_DIR_ENTRY_OFFSET_TERM) {
2415                 EXT4_MP_UNLOCK(d->f.mp);
2416                 return 0;
2417         }
2418
2419         r = ext4_fs_get_inode_ref(&d->f.mp->fs, d->f.inode, &dir);
2420         if (r != EOK) {
2421                 goto Finish;
2422         }
2423
2424         r = ext4_dir_iterator_init(&it, &dir, d->next_off);
2425         if (r != EOK) {
2426                 ext4_fs_put_inode_ref(&dir);
2427                 goto Finish;
2428         }
2429
2430         memcpy(&d->de, it.current, sizeof(ext4_direntry));
2431         de = &d->de;
2432
2433         ext4_dir_iterator_next(&it);
2434
2435         d->next_off =
2436             it.current ? it.current_offset : EXT4_DIR_ENTRY_OFFSET_TERM;
2437
2438         ext4_dir_iterator_fini(&it);
2439         ext4_fs_put_inode_ref(&dir);
2440
2441 Finish:
2442         EXT4_MP_UNLOCK(d->f.mp);
2443         return de;
2444 }
2445
2446 void ext4_dir_entry_rewind(ext4_dir *d)
2447 {
2448     d->next_off = 0;
2449 }
2450
2451 /**
2452  * @}
2453  */