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