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