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