78b920379639e62a2cc27aab850a266c689cbeb2
[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 unaligned;
1258         uint32_t iblock_idx;
1259         uint32_t iblock_last;
1260         uint32_t block_size;
1261
1262         ext4_fsblk_t fblock;
1263         ext4_fsblk_t fblock_start;
1264         uint32_t fblock_count;
1265
1266         uint8_t *u8_buf = buf;
1267         int r;
1268         struct ext4_block b;
1269         struct ext4_inode_ref ref;
1270
1271         ext4_assert(f && f->mp);
1272
1273         if (f->flags & O_WRONLY)
1274                 return EPERM;
1275
1276         if (!size)
1277                 return EOK;
1278
1279         EXT4_MP_LOCK(f->mp);
1280
1281         if (rcnt)
1282                 *rcnt = 0;
1283
1284         r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
1285         if (r != EOK) {
1286                 EXT4_MP_UNLOCK(f->mp);
1287                 return r;
1288         }
1289
1290         /*Sync file size*/
1291         f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);
1292
1293         block_size = ext4_sb_get_block_size(&f->mp->fs.sb);
1294         size = size > (f->fsize - f->fpos) ? (f->fsize - f->fpos) : size;
1295
1296         iblock_idx = (f->fpos) / block_size;
1297         iblock_last = (f->fpos + size) / block_size;
1298         unaligned = (f->fpos) % block_size;
1299
1300         /*If the size of symlink is smaller than 60 bytes*/
1301         if (ext4_inode_is_type(&f->mp->fs.sb, ref.inode, EXT4_INODE_MODE_SOFTLINK)
1302                         && f->fsize < sizeof(ref.inode->blocks)
1303                         && !ext4_inode_get_blocks_count(&f->mp->fs.sb, ref.inode)) {
1304                 char *content = (char *)ref.inode->blocks;
1305                 if (f->fpos < f->fsize) {
1306                         r = (unaligned + size > f->fsize)
1307                                 ?(f->fsize - unaligned)
1308                                 :(size);
1309                         memcpy(buf, content + unaligned, r);
1310                         if (rcnt)
1311                                 *rcnt = r;
1312
1313                 } else {
1314                         r = 0;
1315                         if (rcnt)
1316                                 *rcnt = 0;
1317
1318                 }
1319
1320                 r = EOK;
1321                 goto Finish;
1322         }
1323
1324         if (unaligned) {
1325                 uint32_t l = size > (block_size - unaligned) ?
1326                                 (block_size - unaligned) : size;
1327                 r = ext4_fs_get_inode_data_block_index(&ref, iblock_idx, &fblock,
1328                                 true);
1329                 if (r != EOK)
1330                         goto Finish;
1331
1332                 /* Do we get an unwritten range? */
1333                 if (fblock != 0) {
1334                         r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
1335                         if (r != EOK)
1336                                 goto Finish;
1337
1338                         memcpy(u8_buf, b.data + unaligned, l);
1339
1340                         r = ext4_block_set(f->mp->fs.bdev, &b);
1341                         if (r != EOK)
1342                                 goto Finish;
1343                 } else {
1344                         /* Yes, we do. */
1345                         memset(u8_buf, 0, l);
1346                 }
1347
1348                 u8_buf += l;
1349                 size -= l;
1350                 f->fpos += l;
1351
1352                 if (rcnt)
1353                         *rcnt += l;
1354
1355                 iblock_idx++;
1356         }
1357
1358         fblock_start = 0;
1359         fblock_count = 0;
1360         while (size >= block_size) {
1361                 while (iblock_idx < iblock_last) {
1362                         r = ext4_fs_get_inode_data_block_index(&ref, iblock_idx,
1363                                                                &fblock, true);
1364                         if (r != EOK)
1365                                 goto Finish;
1366
1367                         iblock_idx++;
1368
1369                         if (!fblock_start)
1370                                 fblock_start = fblock;
1371
1372                         if ((fblock_start + fblock_count) != fblock)
1373                                 break;
1374
1375                         fblock_count++;
1376                 }
1377
1378                 r = ext4_blocks_get_direct(f->mp->fs.bdev, u8_buf, fblock_start,
1379                                            fblock_count);
1380                 if (r != EOK)
1381                         goto Finish;
1382
1383                 size -= block_size * fblock_count;
1384                 u8_buf += block_size * fblock_count;
1385                 f->fpos += block_size * fblock_count;
1386
1387                 if (rcnt)
1388                         *rcnt += block_size * fblock_count;
1389
1390                 fblock_start = fblock;
1391                 fblock_count = 1;
1392         }
1393
1394         if (size) {
1395                 r = ext4_fs_get_inode_data_block_index(&ref, iblock_idx, &fblock,
1396                                 true);
1397                 if (r != EOK)
1398                         goto Finish;
1399
1400                 r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
1401                 if (r != EOK)
1402                         goto Finish;
1403
1404                 memcpy(u8_buf, b.data, size);
1405
1406                 r = ext4_block_set(f->mp->fs.bdev, &b);
1407                 if (r != EOK)
1408                         goto Finish;
1409
1410                 f->fpos += size;
1411
1412                 if (rcnt)
1413                         *rcnt += size;
1414         }
1415
1416 Finish:
1417         ext4_fs_put_inode_ref(&ref);
1418         EXT4_MP_UNLOCK(f->mp);
1419         return r;
1420 }
1421
1422 int ext4_fwrite(ext4_file *f, const void *buf, size_t size, size_t *wcnt)
1423 {
1424         uint32_t unaligned;
1425         uint32_t iblock_idx;
1426         uint32_t iblock_last;
1427         uint32_t ifile_blocks;
1428         uint32_t block_size;
1429
1430         uint32_t fblock_count;
1431         ext4_fsblk_t fblock;
1432         ext4_fsblk_t fblock_start;
1433
1434         struct ext4_block b;
1435         struct ext4_inode_ref ref;
1436         const uint8_t *u8_buf = buf;
1437         int r, rr = EOK;
1438
1439         ext4_assert(f && f->mp);
1440
1441         if (f->flags & O_RDONLY)
1442                 return EPERM;
1443
1444         if (!size)
1445                 return EOK;
1446
1447         EXT4_MP_LOCK(f->mp);
1448
1449         if (wcnt)
1450                 *wcnt = 0;
1451
1452         r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
1453         if (r != EOK) {
1454                 EXT4_MP_UNLOCK(f->mp);
1455                 return r;
1456         }
1457
1458         /*Sync file size*/
1459         f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);
1460         block_size = ext4_sb_get_block_size(&f->mp->fs.sb);
1461
1462         iblock_last = (f->fpos + size) / block_size;
1463         iblock_idx = (f->fpos) / block_size;
1464         ifile_blocks = (f->fsize + block_size - 1) / block_size;
1465
1466         unaligned = (f->fpos) % block_size;
1467
1468         if (unaligned) {
1469                 uint32_t l = size > (block_size - unaligned) ?
1470                                 (block_size - unaligned) : size;
1471                 r = ext4_fs_init_inode_data_block_index(&ref, iblock_idx, &fblock);
1472                 if (r != EOK)
1473                         goto Finish;
1474
1475                 r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
1476                 if (r != EOK)
1477                         goto Finish;
1478
1479                 memcpy(b.data + unaligned, u8_buf, l);
1480                 b.dirty = true;
1481
1482                 r = ext4_block_set(f->mp->fs.bdev, &b);
1483                 if (r != EOK)
1484                         goto Finish;
1485
1486                 u8_buf += l;
1487                 size -= l;
1488                 f->fpos += l;
1489
1490                 if (wcnt)
1491                         *wcnt += l;
1492
1493                 iblock_idx++;
1494         }
1495
1496         /*Start write back cache mode.*/
1497         r = ext4_block_cache_write_back(f->mp->fs.bdev, 1);
1498         if (r != EOK)
1499                 goto Finish;
1500
1501         fblock_start = 0;
1502         fblock_count = 0;
1503         while (size >= block_size) {
1504
1505                 while (iblock_idx < iblock_last) {
1506                         if (iblock_idx < ifile_blocks) {
1507                                 r = ext4_fs_init_inode_data_block_index(
1508                                     &ref, iblock_idx, &fblock);
1509                                 if (r != EOK)
1510                                         goto Finish;
1511                         } else {
1512                                 rr = ext4_fs_append_inode_block(&ref, &fblock,
1513                                                                &iblock_idx);
1514                                 if (rr != EOK) {
1515                                         /* Unable to append more blocks. But some
1516                                          * block might be allocated already.
1517                                          * */
1518                                         break;
1519                                 }
1520                         }
1521
1522                         iblock_idx++;
1523
1524                         if (!fblock_start) {
1525                                 fblock_start = fblock;
1526                         }
1527
1528                         if ((fblock_start + fblock_count) != fblock)
1529                                 break;
1530
1531                         fblock_count++;
1532                 }
1533
1534                 r = ext4_blocks_set_direct(f->mp->fs.bdev, u8_buf, fblock_start,
1535                                            fblock_count);
1536                 if (r != EOK)
1537                         break;
1538
1539                 size -= block_size * fblock_count;
1540                 u8_buf += block_size * fblock_count;
1541                 f->fpos += block_size * fblock_count;
1542
1543                 if (wcnt)
1544                         *wcnt += block_size * fblock_count;
1545
1546                 fblock_start = fblock;
1547                 fblock_count = 1;
1548
1549                 if (rr != EOK) {
1550                         /*ext4_fs_append_inode_block has failed and no
1551                          * more blocks might be written. But node size
1552                          * should be updated.*/
1553                         r = rr;
1554                         goto out_fsize;
1555                 }
1556         }
1557
1558         /*Stop write back cache mode*/
1559         ext4_block_cache_write_back(f->mp->fs.bdev, 0);
1560
1561         if (r != EOK)
1562                 goto Finish;
1563
1564         if (size) {
1565                 if (iblock_idx < ifile_blocks) {
1566                         r = ext4_fs_init_inode_data_block_index(&ref, iblock_idx,
1567                                                                &fblock);
1568                         if (r != EOK)
1569                                 goto Finish;
1570                 } else {
1571                         r = ext4_fs_append_inode_block(&ref, &fblock, &iblock_idx);
1572                         if (r != EOK)
1573                                 /*Node size sholud be updated.*/
1574                                 goto out_fsize;
1575                 }
1576
1577                 r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
1578                 if (r != EOK)
1579                         goto Finish;
1580
1581                 memcpy(b.data, u8_buf, size);
1582                 b.dirty = true;
1583
1584                 r = ext4_block_set(f->mp->fs.bdev, &b);
1585                 if (r != EOK)
1586                         goto Finish;
1587
1588                 f->fpos += size;
1589
1590                 if (wcnt)
1591                         *wcnt += size;
1592         }
1593
1594 out_fsize:
1595         if (f->fpos > f->fsize) {
1596                 f->fsize = f->fpos;
1597                 ext4_inode_set_size(ref.inode, f->fsize);
1598                 ref.dirty = true;
1599         }
1600
1601 Finish:
1602         ext4_fs_put_inode_ref(&ref);
1603         EXT4_MP_UNLOCK(f->mp);
1604         return r;
1605 }
1606
1607 int ext4_fseek(ext4_file *f, uint64_t offset, uint32_t origin)
1608 {
1609         switch (origin) {
1610         case SEEK_SET:
1611                 if (offset > f->fsize)
1612                         return EINVAL;
1613
1614                 f->fpos = offset;
1615                 return EOK;
1616         case SEEK_CUR:
1617                 if ((offset + f->fpos) > f->fsize)
1618                         return EINVAL;
1619
1620                 f->fpos += offset;
1621                 return EOK;
1622         case SEEK_END:
1623                 if (offset > f->fsize)
1624                         return EINVAL;
1625
1626                 f->fpos = f->fsize - offset;
1627                 return EOK;
1628         }
1629         return EINVAL;
1630 }
1631
1632 uint64_t ext4_ftell(ext4_file *f) { return f->fpos; }
1633
1634 uint64_t ext4_fsize(ext4_file *f) { return f->fsize; }
1635
1636 int ext4_chmod(const char *path, uint32_t mode)
1637 {
1638         int r;
1639         uint32_t ino;
1640         ext4_file f;
1641         struct ext4_sblock *sb;
1642         struct ext4_inode_ref inode_ref;
1643         struct ext4_mountpoint *mp = ext4_get_mount(path);
1644
1645         if (!mp)
1646                 return ENOENT;
1647
1648         EXT4_MP_LOCK(mp);
1649
1650         r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DIRENTRY_UNKNOWN, 0, 0);
1651         if (r != EOK) {
1652                 EXT4_MP_UNLOCK(mp);
1653                 return r;
1654         }
1655         ino = f.inode;
1656         sb = &mp->fs.sb;
1657         ext4_fclose(&f);
1658         r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
1659         if (r != EOK) {
1660                 EXT4_MP_UNLOCK(mp);
1661                 return r;
1662         }
1663
1664         ext4_inode_set_mode(sb, inode_ref.inode, mode);
1665         inode_ref.dirty = true;
1666
1667         ext4_fs_put_inode_ref(&inode_ref);
1668         EXT4_MP_UNLOCK(mp);
1669         return r;
1670 }
1671
1672 int ext4_chown(const char *path, uint32_t uid, uint32_t gid)
1673 {
1674         int r;
1675         ext4_file f;
1676         uint32_t ino;
1677         struct ext4_inode_ref inode_ref;
1678         struct ext4_mountpoint *mp = ext4_get_mount(path);
1679
1680         if (!mp)
1681                 return ENOENT;
1682
1683         EXT4_MP_LOCK(mp);
1684
1685         r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DIRENTRY_UNKNOWN, 0, 0);
1686         if (r != EOK) {
1687                 EXT4_MP_UNLOCK(mp);
1688                 return r;
1689         }
1690         ino = f.inode;
1691         ext4_fclose(&f);
1692         r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
1693         if (r != EOK) {
1694                 EXT4_MP_UNLOCK(mp);
1695                 return r;
1696         }
1697
1698         ext4_inode_set_uid(inode_ref.inode, uid);
1699         ext4_inode_set_gid(inode_ref.inode, gid);
1700         inode_ref.dirty = true;
1701
1702         ext4_fs_put_inode_ref(&inode_ref);
1703         EXT4_MP_UNLOCK(mp);
1704         return r;
1705 }
1706
1707 int ext4_file_set_atime(const char *path, uint32_t atime)
1708 {
1709         int r;
1710         ext4_file f;
1711         uint32_t ino;
1712         struct ext4_inode_ref inode_ref;
1713         struct ext4_mountpoint *mp = ext4_get_mount(path);
1714
1715         if (!mp)
1716                 return ENOENT;
1717
1718         EXT4_MP_LOCK(mp);
1719
1720         r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DIRENTRY_UNKNOWN, 0, 0);
1721         if (r != EOK) {
1722                 EXT4_MP_UNLOCK(mp);
1723                 return r;
1724         }
1725         ino = f.inode;
1726         ext4_fclose(&f);
1727         r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
1728         if (r != EOK) {
1729                 EXT4_MP_UNLOCK(mp);
1730                 return r;
1731         }
1732
1733         ext4_inode_set_access_time(inode_ref.inode, atime);
1734         inode_ref.dirty = true;
1735
1736         ext4_fs_put_inode_ref(&inode_ref);
1737         EXT4_MP_UNLOCK(mp);
1738         return r;
1739 }
1740
1741 int ext4_file_set_mtime(const char *path, uint32_t mtime)
1742 {
1743         int r;
1744         ext4_file f;
1745         uint32_t ino;
1746         struct ext4_inode_ref inode_ref;
1747         struct ext4_mountpoint *mp = ext4_get_mount(path);
1748
1749         if (!mp)
1750                 return ENOENT;
1751
1752         EXT4_MP_LOCK(mp);
1753
1754         r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DIRENTRY_UNKNOWN, 0, 0);
1755         if (r != EOK) {
1756                 EXT4_MP_UNLOCK(mp);
1757                 return r;
1758         }
1759         ino = f.inode;
1760         ext4_fclose(&f);
1761         r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
1762         if (r != EOK) {
1763                 EXT4_MP_UNLOCK(mp);
1764                 return r;
1765         }
1766
1767         ext4_inode_set_modification_time(inode_ref.inode, mtime);
1768         inode_ref.dirty = true;
1769
1770         ext4_fs_put_inode_ref(&inode_ref);
1771         EXT4_MP_UNLOCK(mp);
1772         return r;
1773 }
1774
1775 int ext4_file_set_ctime(const char *path, uint32_t ctime)
1776 {
1777         int r;
1778         ext4_file f;
1779         uint32_t ino;
1780         struct ext4_inode_ref inode_ref;
1781         struct ext4_mountpoint *mp = ext4_get_mount(path);
1782
1783         if (!mp)
1784                 return ENOENT;
1785
1786         EXT4_MP_LOCK(mp);
1787
1788         r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DIRENTRY_UNKNOWN, 0, 0);
1789         if (r != EOK) {
1790                 EXT4_MP_UNLOCK(mp);
1791                 return r;
1792         }
1793         ino = f.inode;
1794         ext4_fclose(&f);
1795         r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
1796         if (r != EOK) {
1797                 EXT4_MP_UNLOCK(mp);
1798                 return r;
1799         }
1800
1801         ext4_inode_set_change_inode_time(inode_ref.inode, ctime);
1802         inode_ref.dirty = true;
1803
1804         ext4_fs_put_inode_ref(&inode_ref);
1805         EXT4_MP_UNLOCK(mp);
1806         return r;
1807 }
1808
1809 static int ext4_fsymlink_set(ext4_file *f, const void *buf, uint32_t size)
1810 {
1811         struct ext4_block b;
1812         struct ext4_inode_ref ref;
1813         uint32_t sblock;
1814         ext4_fsblk_t fblock;
1815         uint32_t block_size;
1816         int r;
1817
1818         ext4_assert(f && f->mp);
1819
1820         if (!size)
1821                 return EOK;
1822
1823         r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
1824         if (r != EOK) {
1825                 EXT4_MP_UNLOCK(f->mp);
1826                 return r;
1827         }
1828
1829         /*Sync file size*/
1830         block_size = ext4_sb_get_block_size(&f->mp->fs.sb);
1831         if (size > block_size) {
1832                 r = EINVAL;
1833                 goto Finish;
1834         }
1835         r = ext4_ftruncate_no_lock(f, 0);
1836         if (r != EOK)
1837                 goto Finish;
1838
1839         /*Start write back cache mode.*/
1840         r = ext4_block_cache_write_back(f->mp->fs.bdev, 1);
1841         if (r != EOK)
1842                 goto Finish;
1843
1844         /*If the size of symlink is smaller than 60 bytes*/
1845         if (size < sizeof(ref.inode->blocks)) {
1846                 memset(ref.inode->blocks, 0, sizeof(ref.inode->blocks));
1847                 memcpy(ref.inode->blocks, buf, size);
1848                 ext4_inode_clear_flag(ref.inode, EXT4_INODE_FLAG_EXTENTS);
1849         } else {
1850                 ext4_fs_inode_blocks_init(&f->mp->fs, &ref);
1851                 r = ext4_fs_append_inode_block(&ref, &fblock, &sblock);
1852                 if (r != EOK)
1853                         goto Finish;
1854
1855                 r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
1856                 if (r != EOK)
1857                         goto Finish;
1858
1859                 memcpy(b.data, buf, size);
1860                 b.dirty = true;
1861                 r = ext4_block_set(f->mp->fs.bdev, &b);
1862                 if (r != EOK)
1863                         goto Finish;
1864         }
1865
1866         /*Stop write back cache mode*/
1867         ext4_block_cache_write_back(f->mp->fs.bdev, 0);
1868
1869         if (r != EOK)
1870                 goto Finish;
1871
1872         ext4_inode_set_size(ref.inode, size);
1873         ref.dirty = true;
1874
1875         f->fsize = size;
1876         if (f->fpos > size)
1877                 f->fpos = size;
1878
1879 Finish:
1880         ext4_fs_put_inode_ref(&ref);
1881         return r;
1882 }
1883
1884 int ext4_fsymlink(const char *target, const char *path)
1885 {
1886         struct ext4_mountpoint *mp = ext4_get_mount(path);
1887         int r;
1888         ext4_file f;
1889         int filetype;
1890
1891         if (!mp)
1892                 return ENOENT;
1893
1894         filetype = EXT4_DIRENTRY_SYMLINK;
1895
1896         EXT4_MP_LOCK(mp);
1897         ext4_block_cache_write_back(mp->fs.bdev, 1);
1898         r = ext4_generic_open2(&f, path, O_RDWR|O_CREAT, filetype, 0, 0);
1899         if (r == EOK)
1900                 r = ext4_fsymlink_set(&f, target, strlen(target));
1901         else
1902                 goto Finish;
1903
1904         ext4_fclose(&f);
1905
1906 Finish:
1907         ext4_block_cache_write_back(mp->fs.bdev, 0);
1908         EXT4_MP_UNLOCK(mp);
1909         return r;
1910 }
1911
1912 int ext4_readlink(const char *path, char *buf, size_t bufsize, size_t *rcnt)
1913 {
1914         struct ext4_mountpoint *mp = ext4_get_mount(path);
1915         int r;
1916         ext4_file f;
1917         int filetype;
1918
1919         if (!mp)
1920                 return ENOENT;
1921
1922         if (!buf)
1923                 return EINVAL;
1924
1925         filetype = EXT4_DIRENTRY_SYMLINK;
1926
1927         EXT4_MP_LOCK(mp);
1928         ext4_block_cache_write_back(mp->fs.bdev, 1);
1929         r = ext4_generic_open2(&f, path, O_RDONLY, filetype, 0, 0);
1930         if (r == EOK)
1931                 r = ext4_fread(&f, buf, bufsize, rcnt);
1932         else
1933                 goto Finish;
1934
1935         ext4_fclose(&f);
1936
1937 Finish:
1938         ext4_block_cache_write_back(mp->fs.bdev, 0);
1939         EXT4_MP_UNLOCK(mp);
1940         return r;
1941 }
1942
1943 int ext4_setxattr(const char *path, const char *name, size_t name_len,
1944                   const void *data, size_t data_size, bool replace)
1945 {
1946         int r = EOK;
1947         ext4_file f;
1948         uint32_t inode;
1949         uint8_t name_index;
1950         const char *dissected_name = NULL;
1951         size_t dissected_len = 0;
1952         struct ext4_xattr_ref xattr_ref;
1953         struct ext4_inode_ref inode_ref;
1954         struct ext4_mountpoint *mp = ext4_get_mount(path);
1955         if (!mp)
1956                 return ENOENT;
1957
1958         dissected_name = ext4_extract_xattr_name(name, name_len,
1959                                 &name_index, &dissected_len);
1960         if (!dissected_len)
1961                 return EINVAL;
1962
1963         EXT4_MP_LOCK(mp);
1964         r = ext4_generic_open2(&f, path, O_RDWR,
1965                                 EXT4_DIRENTRY_UNKNOWN, 0, 0);
1966         if (r != EOK)
1967                 goto Finish;
1968         inode = f.inode;
1969         ext4_fclose(&f);
1970
1971         r = ext4_fs_get_inode_ref(&mp->fs, inode, &inode_ref);
1972         if (r != EOK)
1973                 goto Finish;
1974
1975         r = ext4_fs_get_xattr_ref(&mp->fs, &inode_ref,
1976                               &xattr_ref);
1977         if (r != EOK) {
1978                 ext4_fs_put_inode_ref(&inode_ref);
1979                 goto Finish;
1980         }
1981
1982         r = ext4_fs_set_xattr(&xattr_ref, name_index, dissected_name,
1983                         dissected_len, data, data_size, replace);
1984
1985         ext4_fs_put_xattr_ref(&xattr_ref);
1986         ext4_fs_put_inode_ref(&inode_ref);
1987 Finish:
1988         EXT4_MP_UNLOCK(mp);
1989         return r;
1990 }
1991
1992 int ext4_getxattr(const char *path, const char *name, size_t name_len,
1993                   void *buf, size_t buf_size, size_t *data_size)
1994 {
1995         int r = EOK;
1996         ext4_file f;
1997         uint32_t inode;
1998         uint8_t name_index;
1999         const char *dissected_name = NULL;
2000         size_t dissected_len = 0;
2001         struct ext4_xattr_ref xattr_ref;
2002         struct ext4_inode_ref inode_ref;
2003         struct ext4_mountpoint *mp = ext4_get_mount(path);
2004         if (!mp)
2005                 return ENOENT;
2006
2007         dissected_name = ext4_extract_xattr_name(name, name_len,
2008                                 &name_index, &dissected_len);
2009         if (!dissected_len)
2010                 return EINVAL;
2011
2012         EXT4_MP_LOCK(mp);
2013         r = ext4_generic_open2(&f, path, O_RDWR,
2014                                 EXT4_DIRENTRY_UNKNOWN, 0, 0);
2015         if (r != EOK)
2016                 goto Finish;
2017         inode = f.inode;
2018         ext4_fclose(&f);
2019
2020         r = ext4_fs_get_inode_ref(&mp->fs, inode, &inode_ref);
2021         if (r != EOK)
2022                 goto Finish;
2023
2024         r = ext4_fs_get_xattr_ref(&mp->fs, &inode_ref,
2025                               &xattr_ref);
2026         if (r != EOK) {
2027                 ext4_fs_put_inode_ref(&inode_ref);
2028                 goto Finish;
2029         }
2030
2031         r = ext4_fs_get_xattr(&xattr_ref, name_index,
2032                                 dissected_name, dissected_len,
2033                                 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 prefix_len;
2058                 const char *prefix;
2059                 prefix = ext4_get_xattr_name_prefix(item->name_index,
2060                                         &prefix_len);
2061                 if (lxi->ret_size +
2062                         prefix_len + item->name_len + 1 >
2063                         lxi->size) {
2064
2065                         lxi->list_too_small = 1;
2066                         return EXT4_XATTR_ITERATE_STOP;
2067                 }
2068                 if (prefix) {
2069                         memcpy(lxi->list_ptr, prefix, prefix_len);
2070                         lxi->list_ptr += prefix_len;
2071                         lxi->ret_size += prefix_len;
2072                 }
2073                 memcpy(lxi->list_ptr, item->name, item->name_len);
2074                 lxi->list_ptr[item->name_len] = 0;
2075                 lxi->list_ptr += item->name_len + 1;
2076         }
2077         lxi->ret_size += item->name_len + 1;
2078         return EXT4_XATTR_ITERATE_CONT;
2079 }
2080
2081 int ext4_listxattr(const char *path, char *list, size_t size, size_t *ret_size)
2082 {
2083         int r = EOK;
2084         ext4_file f;
2085         uint32_t inode;
2086         struct ext4_xattr_ref xattr_ref;
2087         struct ext4_inode_ref inode_ref;
2088         struct ext4_listxattr_iterator lxi;
2089         struct ext4_mountpoint *mp = ext4_get_mount(path);
2090         if (!mp)
2091                 return ENOENT;
2092
2093         lxi.list = list;
2094         lxi.list_ptr = list;
2095         lxi.size = size;
2096         lxi.ret_size = 0;
2097         lxi.list_too_small = false;
2098         lxi.get_required_size = (!size) ? true : false;
2099
2100         EXT4_MP_LOCK(mp);
2101         r = ext4_generic_open2(&f, path, O_RDWR,
2102                                 EXT4_DIRENTRY_UNKNOWN, 0, 0);
2103         if (r != EOK)
2104                 goto Finish;
2105         inode = f.inode;
2106         ext4_fclose(&f);
2107
2108         r = ext4_fs_get_inode_ref(&mp->fs, inode, &inode_ref);
2109         if (r != EOK)
2110                 goto Finish;
2111
2112         r = ext4_fs_get_xattr_ref(&mp->fs, &inode_ref,
2113                               &xattr_ref);
2114         if (r != EOK) {
2115                 ext4_fs_put_inode_ref(&inode_ref);
2116                 goto Finish;
2117         }
2118
2119         xattr_ref.iter_arg = &lxi;
2120         ext4_fs_xattr_iterate(&xattr_ref, ext4_iterate_ea_list);
2121         if (lxi.list_too_small)
2122                 r = ERANGE;
2123
2124         if (r == EOK) {
2125                 if (ret_size)
2126                         *ret_size = lxi.ret_size;
2127
2128         }
2129         ext4_fs_put_xattr_ref(&xattr_ref);
2130         ext4_fs_put_inode_ref(&inode_ref);
2131 Finish:
2132         EXT4_MP_UNLOCK(mp);
2133         return r;
2134
2135 }
2136
2137 int ext4_removexattr(const char *path, const char *name, size_t name_len)
2138 {
2139         int r = EOK;
2140         ext4_file f;
2141         uint32_t inode;
2142         uint8_t name_index;
2143         const char *dissected_name = NULL;
2144         size_t dissected_len = 0;
2145         struct ext4_xattr_ref xattr_ref;
2146         struct ext4_inode_ref inode_ref;
2147         struct ext4_mountpoint *mp = ext4_get_mount(path);
2148         if (!mp)
2149                 return ENOENT;
2150
2151         dissected_name = ext4_extract_xattr_name(name, name_len,
2152                                 &name_index, &dissected_len);
2153         if (!dissected_len)
2154                 return EINVAL;
2155
2156         EXT4_MP_LOCK(mp);
2157         r = ext4_generic_open2(&f, path, O_RDWR,
2158                                 EXT4_DIRENTRY_UNKNOWN, 0, 0);
2159         if (r != EOK)
2160                 goto Finish;
2161         inode = f.inode;
2162         ext4_fclose(&f);
2163
2164         r = ext4_fs_get_inode_ref(&mp->fs, inode, &inode_ref);
2165         if (r != EOK)
2166                 goto Finish;
2167
2168         r = ext4_fs_get_xattr_ref(&mp->fs, &inode_ref,
2169                               &xattr_ref);
2170         if (r != EOK) {
2171                 ext4_fs_put_inode_ref(&inode_ref);
2172                 goto Finish;
2173         }
2174
2175         r = ext4_fs_remove_xattr(&xattr_ref, name_index, dissected_name,
2176                                 dissected_len);
2177
2178         ext4_fs_put_xattr_ref(&xattr_ref);
2179         ext4_fs_put_inode_ref(&inode_ref);
2180 Finish:
2181         EXT4_MP_UNLOCK(mp);
2182         return r;
2183
2184 }
2185
2186 /*********************************DIRECTORY OPERATION************************/
2187
2188 int ext4_dir_rm(const char *path)
2189 {
2190         int r;
2191         int len;
2192         ext4_file f;
2193
2194         struct ext4_mountpoint *mp = ext4_get_mount(path);
2195         struct ext4_inode_ref current;
2196         struct ext4_inode_ref child;
2197         struct ext4_directory_iterator it;
2198
2199         uint32_t name_off;
2200         uint32_t inode_up;
2201         uint32_t inode_current;
2202         uint32_t depth = 1;
2203
2204         bool has_children;
2205         bool is_goal;
2206         bool dir_end;
2207
2208         if (!mp)
2209                 return ENOENT;
2210
2211         EXT4_MP_LOCK(mp);
2212
2213         /*Check if exist.*/
2214         r = ext4_generic_open(&f, path, "r", false, &inode_up, &name_off);
2215         if (r != EOK) {
2216                 EXT4_MP_UNLOCK(mp);
2217                 return r;
2218         }
2219
2220         path += name_off;
2221         len = ext4_path_check(path, &is_goal);
2222
2223         inode_current = f.inode;
2224         dir_end = false;
2225
2226         ext4_block_cache_write_back(mp->fs.bdev, 1);
2227
2228         do {
2229                 /*Load directory node.*/
2230                 r = ext4_fs_get_inode_ref(&f.mp->fs, inode_current, &current);
2231                 if (r != EOK) {
2232                         break;
2233                 }
2234
2235                 /*Initialize iterator.*/
2236                 r = ext4_dir_iterator_init(&it, &current, 0);
2237                 if (r != EOK) {
2238                         ext4_fs_put_inode_ref(&current);
2239                         break;
2240                 }
2241
2242                 while (r == EOK) {
2243
2244                         if (!it.current) {
2245                                 dir_end = true;
2246                                 break;
2247                         }
2248
2249                         /*Get up directory inode when ".." entry*/
2250                         if ((it.current->name_length == 2) &&
2251                             ext4_is_dots(it.current->name,
2252                                          it.current->name_length)) {
2253                                 inode_up = ext4_dir_entry_ll_get_inode(it.current);
2254                         }
2255
2256                         /*If directory or file entry,  but not "." ".." entry*/
2257                         if (!ext4_is_dots(it.current->name,
2258                                           it.current->name_length)) {
2259
2260                                 /*Get child inode reference do unlink
2261                                  * directory/file.*/
2262                                 r = ext4_fs_get_inode_ref(&f.mp->fs,
2263                                         ext4_dir_entry_ll_get_inode(it.current),
2264                                         &child);
2265                                 if (r != EOK)
2266                                         break;
2267
2268                                 /*If directory with no leaf children*/
2269                                 r = ext4_has_children(&has_children, &child);
2270                                 if (r != EOK) {
2271                                         ext4_fs_put_inode_ref(&child);
2272                                         break;
2273                                 }
2274
2275                                 if (has_children) {
2276                                         /*Has directory children. Go into this
2277                                          * directory.*/
2278                                         inode_up = inode_current;
2279                                         inode_current = ext4_dir_entry_ll_get_inode(it.current);
2280                                         depth++;
2281                                         ext4_fs_put_inode_ref(&child);
2282                                         break;
2283                                 }
2284
2285                                 /*No children in child directory or file. Just
2286                                  * unlink.*/
2287                                 r = ext4_unlink(f.mp, &current, &child,
2288                                                 (char *)it.current->name,
2289                                                 it.current->name_length);
2290                                 if (r != EOK) {
2291                                         ext4_fs_put_inode_ref(&child);
2292                                         break;
2293                                 }
2294
2295                                 ext4_inode_set_deletion_time(child.inode,
2296                                                              0xFFFFFFFF);
2297                                 ext4_inode_set_links_count(child.inode, 0);
2298                                 child.dirty = true;
2299                                 /*Turncate*/
2300                                 r = ext4_fs_truncate_inode(&child, 0);
2301                                 if (r != EOK) {
2302                                         ext4_fs_put_inode_ref(&child);
2303                                         break;
2304                                 }
2305
2306                                 r = ext4_fs_free_inode(&child);
2307                                 if (r != EOK) {
2308                                         ext4_fs_put_inode_ref(&child);
2309                                         break;
2310                                 }
2311
2312                                 r = ext4_fs_put_inode_ref(&child);
2313                                 if (r != EOK)
2314                                         break;
2315                         }
2316
2317                         r = ext4_dir_iterator_next(&it);
2318                 }
2319
2320                 if (dir_end) {
2321                         /*Directory iterator reached last entry*/
2322                         ext4_has_children(&has_children, &current);
2323                         if (!has_children) {
2324                                 inode_current = inode_up;
2325                                 if (depth)
2326                                         depth--;
2327                         }
2328                         /*Last unlink*/
2329                         if (!depth) {
2330                                 /*Load parent.*/
2331                                 struct ext4_inode_ref parent;
2332                                 r = ext4_fs_get_inode_ref(&f.mp->fs, inode_up,
2333                                                           &parent);
2334                                 if (r != EOK)
2335                                         goto End;
2336
2337                                 /* In this place all directories should be
2338                                  * unlinked.
2339                                  * Last unlink from root of current directory*/
2340                                 r = ext4_unlink(f.mp, &parent, &current,
2341                                                 (char *)path, len);
2342                                 if (r != EOK) {
2343                                         ext4_fs_put_inode_ref(&parent);
2344                                         goto End;
2345                                 }
2346
2347                                 if (ext4_inode_get_links_count(current.inode) ==
2348                                     2) {
2349                                         ext4_inode_set_deletion_time(
2350                                             current.inode, 0xFFFFFFFF);
2351                                         ext4_inode_set_links_count(
2352                                             current.inode, 0);
2353                                         current.dirty = true;
2354                                         /*Turncate*/
2355                                         r = ext4_fs_truncate_inode(&current, 0);
2356                                         if (r != EOK) {
2357                                                 ext4_fs_put_inode_ref(&parent);
2358                                                 goto End;
2359                                         }
2360
2361                                         r = ext4_fs_free_inode(&current);
2362                                         if (r != EOK) {
2363                                                 ext4_fs_put_inode_ref(&parent);
2364                                                 goto End;
2365                                         }
2366                                 }
2367
2368                                 r = ext4_fs_put_inode_ref(&parent);
2369                                 if (r != EOK)
2370                                         goto End;
2371                         }
2372                 }
2373
2374         End:
2375                 ext4_dir_iterator_fini(&it);
2376                 ext4_fs_put_inode_ref(&current);
2377                 dir_end = false;
2378
2379                 /*When something goes wrong. End loop.*/
2380                 if (r != EOK)
2381                         break;
2382
2383         } while (depth);
2384
2385         ext4_block_cache_write_back(mp->fs.bdev, 0);
2386         EXT4_MP_UNLOCK(mp);
2387         return r;
2388 }
2389
2390 int ext4_dir_mk(const char *path)
2391 {
2392         int r;
2393         ext4_file f;
2394
2395         struct ext4_mountpoint *mp = ext4_get_mount(path);
2396
2397         if (!mp)
2398                 return ENOENT;
2399
2400         EXT4_MP_LOCK(mp);
2401
2402         /*Check if exist.*/
2403         r = ext4_generic_open(&f, path, "r", false, 0, 0);
2404         if (r == EOK) {
2405                 /*Directory already created*/
2406                 EXT4_MP_UNLOCK(mp);
2407                 return r;
2408         }
2409
2410         /*Create new dir*/
2411         r = ext4_generic_open(&f, path, "w", false, 0, 0);
2412         if (r != EOK) {
2413                 EXT4_MP_UNLOCK(mp);
2414                 return r;
2415         }
2416
2417         EXT4_MP_UNLOCK(mp);
2418         return r;
2419 }
2420
2421 int ext4_dir_open(ext4_dir *d, const char *path)
2422 {
2423         struct ext4_mountpoint *mp = ext4_get_mount(path);
2424         int r;
2425
2426         if (!mp)
2427                 return ENOENT;
2428
2429         EXT4_MP_LOCK(mp);
2430         r = ext4_generic_open(&d->f, path, "r", false, 0, 0);
2431         d->next_off = 0;
2432         EXT4_MP_UNLOCK(mp);
2433         return r;
2434 }
2435
2436 int ext4_dir_close(ext4_dir *d)
2437 {
2438     return ext4_fclose(&d->f);
2439 }
2440
2441 const ext4_direntry *ext4_dir_entry_next(ext4_dir *d)
2442 {
2443 #define EXT4_DIR_ENTRY_OFFSET_TERM (uint64_t)(-1)
2444
2445         int r;
2446         ext4_direntry *de = 0;
2447         struct ext4_inode_ref dir;
2448         struct ext4_directory_iterator it;
2449
2450         EXT4_MP_LOCK(d->f.mp);
2451
2452         if (d->next_off == EXT4_DIR_ENTRY_OFFSET_TERM) {
2453                 EXT4_MP_UNLOCK(d->f.mp);
2454                 return 0;
2455         }
2456
2457         r = ext4_fs_get_inode_ref(&d->f.mp->fs, d->f.inode, &dir);
2458         if (r != EOK) {
2459                 goto Finish;
2460         }
2461
2462         r = ext4_dir_iterator_init(&it, &dir, d->next_off);
2463         if (r != EOK) {
2464                 ext4_fs_put_inode_ref(&dir);
2465                 goto Finish;
2466         }
2467
2468         memcpy(&d->de, it.current, sizeof(ext4_direntry));
2469         de = &d->de;
2470
2471         ext4_dir_iterator_next(&it);
2472
2473         d->next_off =
2474             it.current ? it.current_offset : EXT4_DIR_ENTRY_OFFSET_TERM;
2475
2476         ext4_dir_iterator_fini(&it);
2477         ext4_fs_put_inode_ref(&dir);
2478
2479 Finish:
2480         EXT4_MP_UNLOCK(d->f.mp);
2481         return de;
2482 }
2483
2484 void ext4_dir_entry_rewind(ext4_dir *d)
2485 {
2486     d->next_off = 0;
2487 }
2488
2489 /**
2490  * @}
2491  */