ext4: remove dynamic block cache allocation in ext4_mount
[lwext4.git] / src / 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_types.h"
39 #include "ext4_misc.h"
40 #include "ext4_errno.h"
41 #include "ext4_oflags.h"
42 #include "ext4_debug.h"
43
44 #include "ext4.h"
45 #include "ext4_trans.h"
46 #include "ext4_blockdev.h"
47 #include "ext4_fs.h"
48 #include "ext4_dir.h"
49 #include "ext4_inode.h"
50 #include "ext4_super.h"
51 #include "ext4_block_group.h"
52 #include "ext4_dir_idx.h"
53 #include "ext4_xattr.h"
54 #include "ext4_journal.h"
55
56
57 #include <stdlib.h>
58 #include <string.h>
59
60 /**@brief   Mount point OS dependent lock*/
61 #define EXT4_MP_LOCK(_m)                                                       \
62         do {                                                                   \
63                 if ((_m)->os_locks)                                            \
64                         (_m)->os_locks->lock();                                \
65         } while (0)
66
67 /**@brief   Mount point OS dependent unlock*/
68 #define EXT4_MP_UNLOCK(_m)                                                     \
69         do {                                                                   \
70                 if ((_m)->os_locks)                                            \
71                         (_m)->os_locks->unlock();                              \
72         } while (0)
73
74 /**@brief   Mount point descriptor.*/
75 struct ext4_mountpoint {
76
77         /**@brief   Mount done flag.*/
78         bool mounted;
79
80         /**@brief   Mount point name (@ref ext4_mount)*/
81         char name[CONFIG_EXT4_MAX_MP_NAME + 1];
82
83         /**@brief   OS dependent lock/unlock functions.*/
84         const struct ext4_lock *os_locks;
85
86         /**@brief   Ext4 filesystem internals.*/
87         struct ext4_fs fs;
88
89         /**@brief   JBD fs.*/
90         struct jbd_fs jbd_fs;
91
92         /**@brief   Journal.*/
93         struct jbd_journal jbd_journal;
94
95         /**@brief   Block cache.*/
96         struct ext4_bcache bc;
97 };
98
99 /**@brief   Block devices descriptor.*/
100 struct ext4_block_devices {
101
102         /**@brief   Block device name.*/
103         char name[CONFIG_EXT4_MAX_BLOCKDEV_NAME + 1];
104
105         /**@brief   Block device handle.*/
106         struct ext4_blockdev *bd;
107 };
108
109 /**@brief   Block devices.*/
110 static struct ext4_block_devices s_bdevices[CONFIG_EXT4_BLOCKDEVS_COUNT];
111
112 /**@brief   Mountpoints.*/
113 static struct ext4_mountpoint s_mp[CONFIG_EXT4_MOUNTPOINTS_COUNT];
114
115 int ext4_device_register(struct ext4_blockdev *bd,
116                          const char *dev_name)
117 {
118         ext4_assert(bd && dev_name);
119
120         if (strlen(dev_name) > CONFIG_EXT4_MAX_BLOCKDEV_NAME)
121                 return EINVAL;
122
123         for (size_t i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) {
124                 if (!strcmp(s_bdevices[i].name, dev_name))
125                         return EEXIST;
126         }
127
128         for (size_t i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) {
129                 if (!s_bdevices[i].bd) {
130                         strcpy(s_bdevices[i].name, dev_name);
131                         s_bdevices[i].bd = bd;
132                         return EOK;
133                 }
134         }
135
136         return ENOSPC;
137 }
138
139 int ext4_device_unregister(const char *dev_name)
140 {
141         ext4_assert(dev_name);
142
143         for (size_t i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) {
144                 if (strcmp(s_bdevices[i].name, dev_name))
145                         continue;
146
147                 memset(&s_bdevices[i], 0, sizeof(s_bdevices[i]));
148         }
149
150         return ENOENT;
151 }
152
153 int ext4_device_unregister_all(void)
154 {
155         memset(s_bdevices, 0, sizeof(s_bdevices));
156
157         return EOK;
158 }
159
160 /****************************************************************************/
161
162 static bool ext4_is_dots(const uint8_t *name, size_t name_size)
163 {
164         if ((name_size == 1) && (name[0] == '.'))
165                 return true;
166
167         if ((name_size == 2) && (name[0] == '.') && (name[1] == '.'))
168                 return true;
169
170         return false;
171 }
172
173 static int ext4_has_children(bool *has_children, struct ext4_inode_ref *enode)
174 {
175         struct ext4_sblock *sb = &enode->fs->sb;
176
177         /* Check if node is directory */
178         if (!ext4_inode_is_type(sb, enode->inode, EXT4_INODE_MODE_DIRECTORY)) {
179                 *has_children = false;
180                 return EOK;
181         }
182
183         struct ext4_dir_iter it;
184         int rc = ext4_dir_iterator_init(&it, enode, 0);
185         if (rc != EOK)
186                 return rc;
187
188         /* Find a non-empty directory entry */
189         bool found = false;
190         while (it.curr != NULL) {
191                 if (ext4_dir_en_get_inode(it.curr) != 0) {
192                         uint16_t nsize;
193                         nsize = ext4_dir_en_get_name_len(sb, it.curr);
194                         if (!ext4_is_dots(it.curr->name, nsize)) {
195                                 found = true;
196                                 break;
197                         }
198                 }
199
200                 rc = ext4_dir_iterator_next(&it);
201                 if (rc != EOK) {
202                         ext4_dir_iterator_fini(&it);
203                         return rc;
204                 }
205         }
206
207         rc = ext4_dir_iterator_fini(&it);
208         if (rc != EOK)
209                 return rc;
210
211         *has_children = found;
212
213         return EOK;
214 }
215
216 static int ext4_link(struct ext4_mountpoint *mp, struct ext4_inode_ref *parent,
217                      struct ext4_inode_ref *ch, const char *n,
218                      uint32_t len, bool rename)
219 {
220         /* Check maximum name length */
221         if (len > EXT4_DIRECTORY_FILENAME_LEN)
222                 return EINVAL;
223
224         /* Add entry to parent directory */
225         int r = ext4_dir_add_entry(parent, n, len, ch);
226         if (r != EOK)
227                 return r;
228
229         /* Fill new dir -> add '.' and '..' entries.
230          * Also newly allocated inode should have 0 link count.
231          */
232
233         bool is_dir = ext4_inode_is_type(&mp->fs.sb, ch->inode,
234                                EXT4_INODE_MODE_DIRECTORY);
235         if (is_dir && !rename) {
236
237 #if CONFIG_DIR_INDEX_ENABLE
238                 /* Initialize directory index if supported */
239                 if (ext4_sb_feature_com(&mp->fs.sb, EXT4_FCOM_DIR_INDEX)) {
240                         r = ext4_dir_dx_init(ch, parent);
241                         if (r != EOK)
242                                 return r;
243
244                         ext4_inode_set_flag(ch->inode, EXT4_INODE_FLAG_INDEX);
245                         ch->dirty = true;
246                 } else
247 #endif
248                 {
249                         r = ext4_dir_add_entry(ch, ".", strlen("."), ch);
250                         if (r != EOK) {
251                                 ext4_dir_remove_entry(parent, n, strlen(n));
252                                 return r;
253                         }
254
255                         r = ext4_dir_add_entry(ch, "..", strlen(".."), parent);
256                         if (r != EOK) {
257                                 ext4_dir_remove_entry(parent, n, strlen(n));
258                                 ext4_dir_remove_entry(ch, ".", strlen("."));
259                                 return r;
260                         }
261                 }
262
263                 /*New empty directory. Two links (. and ..) */
264                 ext4_inode_set_links_cnt(ch->inode, 2);
265                 ext4_fs_inode_links_count_inc(parent);
266                 ch->dirty = true;
267                 parent->dirty = true;
268                 return r;
269         }
270         /*
271          * In case we want to rename a directory,
272          * we reset the original '..' pointer.
273          */
274         if (is_dir) {
275                 bool idx;
276                 idx = ext4_inode_has_flag(ch->inode, EXT4_INODE_FLAG_INDEX);
277                 struct ext4_dir_search_result res;
278                 if (!idx) {
279                         r = ext4_dir_find_entry(&res, ch, "..", strlen(".."));
280                         if (r != EOK)
281                                 return EIO;
282
283                         ext4_dir_en_set_inode(res.dentry, parent->index);
284                         ext4_trans_set_block_dirty(res.block.buf);
285                         r = ext4_dir_destroy_result(ch, &res);
286                         if (r != EOK)
287                                 return r;
288
289                 } else {
290 #if CONFIG_DIR_INDEX_ENABLE
291                         r = ext4_dir_dx_reset_parent_inode(ch, parent->index);
292                         if (r != EOK)
293                                 return r;
294
295 #endif
296                 }
297
298                 ext4_fs_inode_links_count_inc(parent);
299                 parent->dirty = true;
300         }
301         if (!rename) {
302                 ext4_fs_inode_links_count_inc(ch);
303                 ch->dirty = true;
304         }
305
306         return r;
307 }
308
309 static int ext4_unlink(struct ext4_mountpoint *mp,
310                        struct ext4_inode_ref *parent,
311                        struct ext4_inode_ref *child, const char *name,
312                        uint32_t name_len)
313 {
314         bool has_children;
315         int rc = ext4_has_children(&has_children, child);
316         if (rc != EOK)
317                 return rc;
318
319         /* Cannot unlink non-empty node */
320         if (has_children)
321                 return ENOTEMPTY;
322
323         /* Remove entry from parent directory */
324         rc = ext4_dir_remove_entry(parent, name, name_len);
325         if (rc != EOK)
326                 return rc;
327
328         bool is_dir = ext4_inode_is_type(&mp->fs.sb, child->inode,
329                                          EXT4_INODE_MODE_DIRECTORY);
330
331         /* If directory - handle links from parent */
332         if (is_dir) {
333                 ext4_fs_inode_links_count_dec(parent);
334                 parent->dirty = true;
335         }
336
337         /*
338          * TODO: Update timestamps of the parent
339          * (when we have wall-clock time).
340          *
341          * ext4_inode_set_change_inode_time(parent->inode, (uint32_t) now);
342          * ext4_inode_set_modification_time(parent->inode, (uint32_t) now);
343          * parent->dirty = true;
344          */
345
346         /*
347          * TODO: Update timestamp for inode.
348          *
349          * ext4_inode_set_change_inode_time(child->inode,
350          *     (uint32_t) now);
351          */
352         if (ext4_inode_get_links_cnt(child->inode)) {
353                 ext4_fs_inode_links_count_dec(child);
354                 child->dirty = true;
355         }
356
357         return EOK;
358 }
359
360 /****************************************************************************/
361
362 int ext4_mount(const char *dev_name, const char *mount_point,
363                bool read_only)
364 {
365         int r;
366         uint32_t bsize;
367         struct ext4_bcache *bc;
368         struct ext4_blockdev *bd = 0;
369         struct ext4_mountpoint *mp = 0;
370
371         ext4_assert(mount_point && dev_name);
372
373         size_t mp_len = strlen(mount_point);
374
375         if (mp_len > CONFIG_EXT4_MAX_MP_NAME)
376                 return EINVAL;
377
378         if (mount_point[mp_len - 1] != '/')
379                 return ENOTSUP;
380
381         for (size_t i = 0; i < CONFIG_EXT4_BLOCKDEVS_COUNT; ++i) {
382                 if (s_bdevices[i].name) {
383                         if (!strcmp(dev_name, s_bdevices[i].name)) {
384                                 bd = s_bdevices[i].bd;
385                                 break;
386                         }
387                 }
388         }
389
390         if (!bd)
391                 return ENODEV;
392
393         for (size_t i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
394                 if (!s_mp[i].mounted) {
395                         strcpy(s_mp[i].name, mount_point);
396                         s_mp[i].mounted = 1;
397                         mp = &s_mp[i];
398                         break;
399                 }
400
401                 if (!strcmp(s_mp[i].name, mount_point))
402                         return EOK;
403         }
404
405         if (!mp)
406                 return ENOMEM;
407
408         r = ext4_block_init(bd);
409         if (r != EOK)
410                 return r;
411
412         r = ext4_fs_init(&mp->fs, bd, read_only);
413         if (r != EOK) {
414                 ext4_block_fini(bd);
415                 return r;
416         }
417
418         bsize = ext4_sb_get_block_size(&mp->fs.sb);
419         ext4_block_set_lb_size(bd, bsize);
420         bc = &mp->bc;
421
422         r = ext4_bcache_init_dynamic(bc, CONFIG_BLOCK_DEV_CACHE_SIZE, bsize);
423         if (r != EOK) {
424                 ext4_block_fini(bd);
425                 return r;
426         }
427
428         if (bsize != bc->itemsize)
429                 return ENOTSUP;
430
431         /*Bind block cache to block device*/
432         r = ext4_block_bind_bcache(bd, bc);
433         if (r != EOK) {
434                 ext4_bcache_cleanup(bc);
435                 ext4_block_fini(bd);
436                 ext4_bcache_fini_dynamic(bc);
437                 return r;
438         }
439
440         bd->fs = &mp->fs;
441         return r;
442 }
443
444
445 int ext4_umount(const char *mount_point)
446 {
447         int i;
448         int r;
449         struct ext4_mountpoint *mp = 0;
450
451         for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
452                 if (!strcmp(s_mp[i].name, mount_point)) {
453                         mp = &s_mp[i];
454                         break;
455                 }
456         }
457
458         if (!mp)
459                 return ENODEV;
460
461         r = ext4_fs_fini(&mp->fs);
462         if (r != EOK)
463                 goto Finish;
464
465         mp->mounted = 0;
466
467         ext4_bcache_cleanup(mp->fs.bdev->bc);
468         ext4_bcache_fini_dynamic(mp->fs.bdev->bc);
469
470         r = ext4_block_fini(mp->fs.bdev);
471 Finish:
472         mp->fs.bdev->fs = NULL;
473         return r;
474 }
475
476 static struct ext4_mountpoint *ext4_get_mount(const char *path)
477 {
478         int i;
479         for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
480
481                 if (!s_mp[i].mounted)
482                         continue;
483
484                 if (!strncmp(s_mp[i].name, path, strlen(s_mp[i].name)))
485                         return &s_mp[i];
486         }
487         return NULL;
488 }
489
490 __unused
491 static int __ext4_journal_start(const char *mount_point)
492 {
493         int r = EOK;
494         struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
495         if (!mp)
496                 return ENOENT;
497
498         if (mp->fs.read_only)
499                 return EOK;
500
501         if (ext4_sb_feature_com(&mp->fs.sb,
502                                 EXT4_FCOM_HAS_JOURNAL)) {
503                 r = jbd_get_fs(&mp->fs, &mp->jbd_fs);
504                 if (r != EOK)
505                         goto Finish;
506
507                 r = jbd_journal_start(&mp->jbd_fs, &mp->jbd_journal);
508                 if (r != EOK) {
509                         mp->jbd_fs.dirty = false;
510                         jbd_put_fs(&mp->jbd_fs);
511                         goto Finish;
512                 }
513                 mp->fs.jbd_fs = &mp->jbd_fs;
514                 mp->fs.jbd_journal = &mp->jbd_journal;
515         }
516 Finish:
517         return r;
518 }
519
520 __unused
521 static int __ext4_journal_stop(const char *mount_point)
522 {
523         int r = EOK;
524         struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
525         if (!mp)
526                 return ENOENT;
527
528         if (mp->fs.read_only)
529                 return EOK;
530
531         if (ext4_sb_feature_com(&mp->fs.sb,
532                                 EXT4_FCOM_HAS_JOURNAL)) {
533                 r = jbd_journal_stop(&mp->jbd_journal);
534                 if (r != EOK) {
535                         mp->jbd_fs.dirty = false;
536                         jbd_put_fs(&mp->jbd_fs);
537                         mp->fs.jbd_journal = NULL;
538                         mp->fs.jbd_fs = NULL;
539                         goto Finish;
540                 }
541
542                 r = jbd_put_fs(&mp->jbd_fs);
543                 if (r != EOK) {
544                         mp->fs.jbd_journal = NULL;
545                         mp->fs.jbd_fs = NULL;
546                         goto Finish;
547                 }
548
549                 mp->fs.jbd_journal = NULL;
550                 mp->fs.jbd_fs = NULL;
551         }
552 Finish:
553         return r;
554 }
555
556 __unused
557 static int __ext4_recover(const char *mount_point)
558 {
559         struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
560         if (!mp)
561                 return ENOENT;
562
563         int r = ENOTSUP;
564         EXT4_MP_LOCK(mp);
565         if (ext4_sb_feature_com(&mp->fs.sb, EXT4_FCOM_HAS_JOURNAL)) {
566                 struct jbd_fs *jbd_fs = ext4_calloc(1, sizeof(struct jbd_fs));
567                 if (!jbd_fs) {
568                          r = ENOMEM;
569                          goto Finish;
570                 }
571
572
573                 r = jbd_get_fs(&mp->fs, jbd_fs);
574                 if (r != EOK) {
575                         ext4_free(jbd_fs);
576                         goto Finish;
577                 }
578
579                 r = jbd_recover(jbd_fs);
580                 jbd_put_fs(jbd_fs);
581                 ext4_free(jbd_fs);
582         }
583         if (r == EOK && !mp->fs.read_only) {
584                 uint32_t bgid;
585                 uint64_t free_blocks_count = 0;
586                 uint32_t free_inodes_count = 0;
587                 struct ext4_block_group_ref bg_ref;
588
589                 /* Update superblock's stats */
590                 for (bgid = 0;bgid < ext4_block_group_cnt(&mp->fs.sb);bgid++) {
591                         r = ext4_fs_get_block_group_ref(&mp->fs, bgid, &bg_ref);
592                         if (r != EOK)
593                                 goto Finish;
594
595                         free_blocks_count +=
596                                 ext4_bg_get_free_blocks_count(bg_ref.block_group,
597                                                 &mp->fs.sb);
598                         free_inodes_count +=
599                                 ext4_bg_get_free_inodes_count(bg_ref.block_group,
600                                                 &mp->fs.sb);
601
602                         ext4_fs_put_block_group_ref(&bg_ref);
603                 }
604                 ext4_sb_set_free_blocks_cnt(&mp->fs.sb, free_blocks_count);
605                 ext4_set32(&mp->fs.sb, free_inodes_count, free_inodes_count);
606                 /* We don't need to save the superblock stats immediately. */
607         }
608
609 Finish:
610         EXT4_MP_UNLOCK(mp);
611         return r;
612 }
613
614 __unused
615 static int __ext4_trans_start(struct ext4_mountpoint *mp)
616 {
617         int r = EOK;
618         if (mp->fs.jbd_journal && !mp->fs.curr_trans) {
619                 struct jbd_journal *journal = mp->fs.jbd_journal;
620                 struct jbd_trans *trans;
621                 trans = jbd_journal_new_trans(journal);
622                 if (!trans) {
623                         r = ENOMEM;
624                         goto Finish;
625                 }
626                 mp->fs.curr_trans = trans;
627         }
628 Finish:
629         return r;
630 }
631
632 __unused
633 static int __ext4_trans_stop(struct ext4_mountpoint *mp)
634 {
635         int r = EOK;
636         if (mp->fs.jbd_journal && mp->fs.curr_trans) {
637                 struct jbd_journal *journal = mp->fs.jbd_journal;
638                 struct jbd_trans *trans = mp->fs.curr_trans;
639                 r = jbd_journal_commit_trans(journal, trans);
640                 mp->fs.curr_trans = NULL;
641         }
642         return r;
643 }
644
645 __unused
646 static void __ext4_trans_abort(struct ext4_mountpoint *mp)
647 {
648         if (mp->fs.jbd_journal && mp->fs.curr_trans) {
649                 struct jbd_journal *journal = mp->fs.jbd_journal;
650                 struct jbd_trans *trans = mp->fs.curr_trans;
651                 jbd_journal_free_trans(journal, trans, true);
652                 mp->fs.curr_trans = NULL;
653         }
654 }
655
656 int ext4_journal_start(const char *mount_point __unused)
657 {
658         int r = EOK;
659 #if CONFIG_JOURNALING_ENABLE
660         r = __ext4_journal_start(mount_point);
661 #endif
662         return r;
663 }
664
665 int ext4_journal_stop(const char *mount_point __unused)
666 {
667         int r = EOK;
668 #if CONFIG_JOURNALING_ENABLE
669         r = __ext4_journal_stop(mount_point);
670 #endif
671         return r;
672 }
673
674 int ext4_recover(const char *mount_point __unused)
675 {
676         int r = EOK;
677 #if CONFIG_JOURNALING_ENABLE
678         r = __ext4_recover(mount_point);
679 #endif
680         return r;
681 }
682
683 static int ext4_trans_start(struct ext4_mountpoint *mp __unused)
684 {
685         int r = EOK;
686 #if CONFIG_JOURNALING_ENABLE
687         r = __ext4_trans_start(mp);
688 #endif
689         return r;
690 }
691
692 static int ext4_trans_stop(struct ext4_mountpoint *mp __unused)
693 {
694         int r = EOK;
695 #if CONFIG_JOURNALING_ENABLE
696         r = __ext4_trans_stop(mp);
697 #endif
698         return r;
699 }
700
701 static void ext4_trans_abort(struct ext4_mountpoint *mp __unused)
702 {
703 #if CONFIG_JOURNALING_ENABLE
704         __ext4_trans_abort(mp);
705 #endif
706 }
707
708
709 int ext4_mount_point_stats(const char *mount_point,
710                            struct ext4_mount_stats *stats)
711 {
712         struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
713
714         if (!mp)
715                 return ENOENT;
716
717         EXT4_MP_LOCK(mp);
718         stats->inodes_count = ext4_get32(&mp->fs.sb, inodes_count);
719         stats->free_inodes_count = ext4_get32(&mp->fs.sb, free_inodes_count);
720         stats->blocks_count = ext4_sb_get_blocks_cnt(&mp->fs.sb);
721         stats->free_blocks_count = ext4_sb_get_free_blocks_cnt(&mp->fs.sb);
722         stats->block_size = ext4_sb_get_block_size(&mp->fs.sb);
723
724         stats->block_group_count = ext4_block_group_cnt(&mp->fs.sb);
725         stats->blocks_per_group = ext4_get32(&mp->fs.sb, blocks_per_group);
726         stats->inodes_per_group = ext4_get32(&mp->fs.sb, inodes_per_group);
727
728         memcpy(stats->volume_name, mp->fs.sb.volume_name, 16);
729         EXT4_MP_UNLOCK(mp);
730
731         return EOK;
732 }
733
734 int ext4_mount_setup_locks(const char *mount_point,
735                            const struct ext4_lock *locks)
736 {
737         uint32_t i;
738         struct ext4_mountpoint *mp = 0;
739
740         for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
741                 if (!strcmp(s_mp[i].name, mount_point)) {
742                         mp = &s_mp[i];
743                         break;
744                 }
745         }
746         if (!mp)
747                 return ENOENT;
748
749         mp->os_locks = locks;
750         return EOK;
751 }
752
753 /********************************FILE OPERATIONS*****************************/
754
755 static int ext4_path_check(const char *path, bool *is_goal)
756 {
757         int i;
758
759         for (i = 0; i < EXT4_DIRECTORY_FILENAME_LEN; ++i) {
760
761                 if (path[i] == '/') {
762                         *is_goal = false;
763                         return i;
764                 }
765
766                 if (path[i] == 0) {
767                         *is_goal = true;
768                         return i;
769                 }
770         }
771
772         return 0;
773 }
774
775 static bool ext4_parse_flags(const char *flags, uint32_t *file_flags)
776 {
777         if (!flags)
778                 return false;
779
780         if (!strcmp(flags, "r") || !strcmp(flags, "rb")) {
781                 *file_flags = O_RDONLY;
782                 return true;
783         }
784
785         if (!strcmp(flags, "w") || !strcmp(flags, "wb")) {
786                 *file_flags = O_WRONLY | O_CREAT | O_TRUNC;
787                 return true;
788         }
789
790         if (!strcmp(flags, "a") || !strcmp(flags, "ab")) {
791                 *file_flags = O_WRONLY | O_CREAT | O_APPEND;
792                 return true;
793         }
794
795         if (!strcmp(flags, "r+") || !strcmp(flags, "rb+") ||
796             !strcmp(flags, "r+b")) {
797                 *file_flags = O_RDWR;
798                 return true;
799         }
800
801         if (!strcmp(flags, "w+") || !strcmp(flags, "wb+") ||
802             !strcmp(flags, "w+b")) {
803                 *file_flags = O_RDWR | O_CREAT | O_TRUNC;
804                 return true;
805         }
806
807         if (!strcmp(flags, "a+") || !strcmp(flags, "ab+") ||
808             !strcmp(flags, "a+b")) {
809                 *file_flags = O_RDWR | O_CREAT | O_APPEND;
810                 return true;
811         }
812
813         return false;
814 }
815
816 static int ext4_trunc_inode(struct ext4_mountpoint *mp,
817                             uint32_t index, uint64_t new_size)
818 {
819         int r = EOK;
820         struct ext4_fs *const fs = &mp->fs;
821         struct ext4_inode_ref inode_ref;
822         uint64_t inode_size;
823         bool has_trans = mp->fs.jbd_journal && mp->fs.curr_trans;
824         r = ext4_fs_get_inode_ref(fs, index, &inode_ref);
825         if (r != EOK)
826                 return r;
827
828         inode_size = ext4_inode_get_size(&fs->sb, inode_ref.inode);
829         ext4_fs_put_inode_ref(&inode_ref);
830         if (has_trans)
831                 ext4_trans_stop(mp);
832
833         while (inode_size > new_size + CONFIG_MAX_TRUNCATE_SIZE) {
834
835                 inode_size -= CONFIG_MAX_TRUNCATE_SIZE;
836
837                 ext4_trans_start(mp);
838                 r = ext4_fs_get_inode_ref(fs, index, &inode_ref);
839                 if (r != EOK) {
840                         ext4_trans_abort(mp);
841                         break;
842                 }
843                 r = ext4_fs_truncate_inode(&inode_ref, inode_size);
844                 if (r != EOK)
845                         ext4_fs_put_inode_ref(&inode_ref);
846                 else
847                         r = ext4_fs_put_inode_ref(&inode_ref);
848
849                 if (r != EOK) {
850                         ext4_trans_abort(mp);
851                         goto Finish;
852                 } else
853                         ext4_trans_stop(mp);
854         }
855
856         if (inode_size > new_size) {
857
858                 inode_size = new_size;
859
860                 ext4_trans_start(mp);
861                 r = ext4_fs_get_inode_ref(fs, index, &inode_ref);
862                 if (r != EOK) {
863                         ext4_trans_abort(mp);
864                         goto Finish;
865                 }
866                 r = ext4_fs_truncate_inode(&inode_ref, inode_size);
867                 if (r != EOK)
868                         ext4_fs_put_inode_ref(&inode_ref);
869                 else
870                         r = ext4_fs_put_inode_ref(&inode_ref);
871
872                 if (r != EOK)
873                         ext4_trans_abort(mp);
874                 else
875                         ext4_trans_stop(mp);
876
877         }
878
879 Finish:
880
881         if (has_trans)
882                 ext4_trans_start(mp);
883
884         return r;
885 }
886
887 static int ext4_trunc_dir(struct ext4_mountpoint *mp,
888                           struct ext4_inode_ref *parent,
889                           struct ext4_inode_ref *dir)
890 {
891         int r = EOK;
892         bool is_dir = ext4_inode_is_type(&mp->fs.sb, dir->inode,
893                         EXT4_INODE_MODE_DIRECTORY);
894         uint32_t block_size = ext4_sb_get_block_size(&mp->fs.sb);
895         if (!is_dir)
896                 return EINVAL;
897
898 #if CONFIG_DIR_INDEX_ENABLE
899         /* Initialize directory index if supported */
900         if (ext4_sb_feature_com(&mp->fs.sb, EXT4_FCOM_DIR_INDEX)) {
901                 r = ext4_dir_dx_init(dir, parent);
902                 if (r != EOK)
903                         return r;
904
905                 r = ext4_trunc_inode(mp, dir->index,
906                                      EXT4_DIR_DX_INIT_BCNT * block_size);
907                 if (r != EOK)
908                         return r;
909         } else
910 #endif
911         {
912                 r = ext4_trunc_inode(mp, dir->index, block_size);
913                 if (r != EOK)
914                         return r;
915         }
916
917         return ext4_fs_truncate_inode(dir, 0);
918 }
919
920 /*
921  * NOTICE: if filetype is equal to EXT4_DIRENTRY_UNKNOWN,
922  * any filetype of the target dir entry will be accepted.
923  */
924 static int ext4_generic_open2(ext4_file *f, const char *path, int flags,
925                               int ftype, uint32_t *parent_inode,
926                               uint32_t *name_off)
927 {
928         bool is_goal = false;
929         uint32_t imode = EXT4_INODE_MODE_DIRECTORY;
930         uint32_t next_inode;
931
932         int r;
933         int len;
934         struct ext4_mountpoint *mp = ext4_get_mount(path);
935         struct ext4_dir_search_result result;
936         struct ext4_inode_ref ref;
937
938         f->mp = 0;
939
940         if (!mp)
941                 return ENOENT;
942
943         struct ext4_fs *const fs = &mp->fs;
944         struct ext4_sblock *const sb = &mp->fs.sb;
945
946         if (fs->read_only && flags & O_CREAT)
947                 return EROFS;
948
949         f->flags = flags;
950
951         /*Skip mount point*/
952         path += strlen(mp->name);
953
954         if (name_off)
955                 *name_off = strlen(mp->name);
956
957         /*Load root*/
958         r = ext4_fs_get_inode_ref(fs, EXT4_INODE_ROOT_INDEX, &ref);
959         if (r != EOK)
960                 return r;
961
962         if (parent_inode)
963                 *parent_inode = ref.index;
964
965         if (flags & O_CREAT)
966                 ext4_trans_start(mp);
967
968         len = ext4_path_check(path, &is_goal);
969         while (1) {
970
971                 len = ext4_path_check(path, &is_goal);
972                 if (!len) {
973                         /*If root open was request.*/
974                         if (ftype == EXT4_DE_DIR || ftype == EXT4_DE_UNKNOWN)
975                                 if (is_goal)
976                                         break;
977
978                         r = ENOENT;
979                         break;
980                 }
981
982                 r = ext4_dir_find_entry(&result, &ref, path, len);
983                 if (r != EOK) {
984
985                         /*Destroy last result*/
986                         ext4_dir_destroy_result(&ref, &result);
987                         if (r != ENOENT)
988                                 break;
989
990                         if (!(f->flags & O_CREAT))
991                                 break;
992
993                         /*O_CREAT allows create new entry*/
994                         struct ext4_inode_ref child_ref;
995                         r = ext4_fs_alloc_inode(fs, &child_ref,
996                                         is_goal ? ftype : EXT4_DE_DIR);
997                         if (r != EOK)
998                                 break;
999
1000
1001                         /*Link with root dir.*/
1002                         r = ext4_link(mp, &ref, &child_ref, path, len, false);
1003                         if (r != EOK) {
1004                                 /*Fail. Free new inode.*/
1005                                 ext4_fs_free_inode(&child_ref);
1006                                 /*We do not want to write new inode.
1007                                   But block has to be released.*/
1008                                 child_ref.dirty = false;
1009                                 ext4_fs_put_inode_ref(&child_ref);
1010                                 break;
1011                         }
1012
1013                         ext4_fs_put_inode_ref(&child_ref);
1014                         continue;
1015                 }
1016
1017                 if (parent_inode)
1018                         *parent_inode = ref.index;
1019
1020                 next_inode = ext4_dir_en_get_inode(result.dentry);
1021                 if (ext4_sb_feature_incom(sb, EXT4_FINCOM_FILETYPE)) {
1022                         uint8_t t;
1023                         t = ext4_dir_en_get_inode_type(sb, result.dentry);
1024                         imode = ext4_fs_correspond_inode_mode(t);
1025                 } else {
1026                         struct ext4_inode_ref child_ref;
1027                         r = ext4_fs_get_inode_ref(fs, next_inode, &child_ref);
1028                         if (r != EOK)
1029                                 break;
1030
1031                         imode = ext4_inode_type(sb, child_ref.inode);
1032                         ext4_fs_put_inode_ref(&child_ref);
1033                 }
1034
1035                 r = ext4_dir_destroy_result(&ref, &result);
1036                 if (r != EOK)
1037                         break;
1038
1039                 /*If expected file error*/
1040                 if (imode != EXT4_INODE_MODE_DIRECTORY && !is_goal) {
1041                         r = ENOENT;
1042                         break;
1043                 }
1044                 if (ftype != EXT4_DE_UNKNOWN) {
1045                         bool df = imode != ext4_fs_correspond_inode_mode(ftype);
1046                         if (df && is_goal) {
1047                                 r = ENOENT;
1048                                 break;
1049                         }
1050                 }
1051
1052                 r = ext4_fs_put_inode_ref(&ref);
1053                 if (r != EOK)
1054                         break;
1055
1056                 r = ext4_fs_get_inode_ref(fs, next_inode, &ref);
1057                 if (r != EOK)
1058                         break;
1059
1060                 if (is_goal)
1061                         break;
1062
1063                 path += len + 1;
1064
1065                 if (name_off)
1066                         *name_off += len + 1;
1067         };
1068
1069         if (r != EOK) {
1070                 ext4_fs_put_inode_ref(&ref);
1071                 return r;
1072         }
1073
1074         if (is_goal) {
1075
1076                 if ((f->flags & O_TRUNC) && (imode == EXT4_INODE_MODE_FILE)) {
1077                         r = ext4_trunc_inode(mp, ref.index, 0);
1078                         if (r != EOK) {
1079                                 ext4_fs_put_inode_ref(&ref);
1080                                 return r;
1081                         }
1082                 }
1083
1084                 f->mp = mp;
1085                 f->fsize = ext4_inode_get_size(sb, ref.inode);
1086                 f->inode = ref.index;
1087                 f->fpos = 0;
1088
1089                 if (f->flags & O_APPEND)
1090                         f->fpos = f->fsize;
1091
1092         }
1093
1094         r = ext4_fs_put_inode_ref(&ref);
1095         if (flags & O_CREAT) {
1096                 if (r == EOK)
1097                         ext4_trans_stop(mp);
1098                 else
1099                         ext4_trans_abort(mp);
1100
1101         }
1102
1103         return r;
1104 }
1105
1106 /****************************************************************************/
1107
1108 static int ext4_generic_open(ext4_file *f, const char *path, const char *flags,
1109                              bool file_expect, uint32_t *parent_inode,
1110                              uint32_t *name_off)
1111 {
1112         uint32_t iflags;
1113         int filetype;
1114         if (ext4_parse_flags(flags, &iflags) == false)
1115                 return EINVAL;
1116
1117         if (file_expect == true)
1118                 filetype = EXT4_DE_REG_FILE;
1119         else
1120                 filetype = EXT4_DE_DIR;
1121
1122         return ext4_generic_open2(f, path, iflags, filetype, parent_inode,
1123                                   name_off);
1124 }
1125
1126 static int ext4_create_hardlink(const char *path,
1127                 struct ext4_inode_ref *child_ref, bool rename)
1128 {
1129         bool is_goal = false;
1130         uint32_t inode_mode = EXT4_INODE_MODE_DIRECTORY;
1131         uint32_t next_inode;
1132
1133         int r;
1134         int len;
1135         struct ext4_mountpoint *mp = ext4_get_mount(path);
1136         struct ext4_dir_search_result result;
1137         struct ext4_inode_ref ref;
1138
1139         if (!mp)
1140                 return ENOENT;
1141
1142         struct ext4_fs *const fs = &mp->fs;
1143         struct ext4_sblock *const sb = &mp->fs.sb;
1144
1145         /*Skip mount point*/
1146         path += strlen(mp->name);
1147
1148         /*Load root*/
1149         r = ext4_fs_get_inode_ref(fs, EXT4_INODE_ROOT_INDEX, &ref);
1150         if (r != EOK)
1151                 return r;
1152
1153         len = ext4_path_check(path, &is_goal);
1154         while (1) {
1155
1156                 len = ext4_path_check(path, &is_goal);
1157                 if (!len) {
1158                         /*If root open was request.*/
1159                         r = is_goal ? EINVAL : ENOENT;
1160                         break;
1161                 }
1162
1163                 r = ext4_dir_find_entry(&result, &ref, path, len);
1164                 if (r != EOK) {
1165
1166                         /*Destroy last result*/
1167                         ext4_dir_destroy_result(&ref, &result);
1168
1169                         if (r != ENOENT || !is_goal)
1170                                 break;
1171
1172                         /*Link with root dir.*/
1173                         r = ext4_link(mp, &ref, child_ref, path, len, rename);
1174                         break;
1175                 } else if (r == EOK && is_goal) {
1176                         /*Destroy last result*/
1177                         ext4_dir_destroy_result(&ref, &result);
1178                         r = EEXIST;
1179                         break;
1180                 }
1181
1182                 next_inode = result.dentry->inode;
1183                 if (ext4_sb_feature_incom(sb, EXT4_FINCOM_FILETYPE)) {
1184                         uint8_t t;
1185                         t = ext4_dir_en_get_inode_type(sb, result.dentry);
1186                         inode_mode = ext4_fs_correspond_inode_mode(t);
1187                 } else {
1188                         struct ext4_inode_ref child_ref;
1189                         r = ext4_fs_get_inode_ref(fs, next_inode, &child_ref);
1190                         if (r != EOK)
1191                                 break;
1192
1193                         inode_mode = ext4_inode_type(sb, child_ref.inode);
1194                         ext4_fs_put_inode_ref(&child_ref);
1195                 }
1196
1197                 r = ext4_dir_destroy_result(&ref, &result);
1198                 if (r != EOK)
1199                         break;
1200
1201                 if (inode_mode != EXT4_INODE_MODE_DIRECTORY) {
1202                         r = is_goal ? EEXIST : ENOENT;
1203                         break;
1204                 }
1205
1206                 r = ext4_fs_put_inode_ref(&ref);
1207                 if (r != EOK)
1208                         break;
1209
1210                 r = ext4_fs_get_inode_ref(fs, next_inode, &ref);
1211                 if (r != EOK)
1212                         break;
1213
1214                 if (is_goal)
1215                         break;
1216
1217                 path += len + 1;
1218         };
1219
1220         if (r != EOK) {
1221                 ext4_fs_put_inode_ref(&ref);
1222                 return r;
1223         }
1224
1225         r = ext4_fs_put_inode_ref(&ref);
1226         return r;
1227 }
1228
1229 static int ext4_remove_orig_reference(const char *path, uint32_t name_off,
1230                                       struct ext4_inode_ref *parent_ref,
1231                                       struct ext4_inode_ref *child_ref)
1232 {
1233         bool is_goal;
1234         int r;
1235         int len;
1236         struct ext4_mountpoint *mp = ext4_get_mount(path);
1237
1238         if (!mp)
1239                 return ENOENT;
1240
1241         /*Set path*/
1242         path += name_off;
1243
1244         len = ext4_path_check(path, &is_goal);
1245
1246         /* Remove entry from parent directory */
1247         r = ext4_dir_remove_entry(parent_ref, path, len);
1248         if (r != EOK)
1249                 goto Finish;
1250
1251         if (ext4_inode_is_type(&mp->fs.sb, child_ref->inode,
1252                                EXT4_INODE_MODE_DIRECTORY)) {
1253                 ext4_fs_inode_links_count_dec(parent_ref);
1254                 parent_ref->dirty = true;
1255         }
1256 Finish:
1257         return r;
1258 }
1259
1260 int ext4_flink(const char *path, const char *hardlink_path)
1261 {
1262         int r;
1263         ext4_file f;
1264         uint32_t name_off;
1265         bool child_loaded = false;
1266         uint32_t parent_inode, child_inode;
1267         struct ext4_mountpoint *mp = ext4_get_mount(path);
1268         struct ext4_mountpoint *target_mp = ext4_get_mount(hardlink_path);
1269         struct ext4_inode_ref child_ref;
1270
1271         if (!mp)
1272                 return ENOENT;
1273
1274         if (mp->fs.read_only)
1275                 return EROFS;
1276
1277         /* Will that happen? Anyway return EINVAL for such case. */
1278         if (mp != target_mp)
1279                 return EINVAL;
1280
1281         EXT4_MP_LOCK(mp);
1282         ext4_trans_start(mp);
1283
1284         r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN,
1285                                &parent_inode, &name_off);
1286         if (r != EOK)
1287                 goto Finish;
1288
1289         child_inode = f.inode;
1290         ext4_fclose(&f);
1291
1292         /*We have file to unlink. Load it.*/
1293         r = ext4_fs_get_inode_ref(&mp->fs, child_inode, &child_ref);
1294         if (r != EOK)
1295                 goto Finish;
1296
1297         child_loaded = true;
1298
1299         /* Creating hardlink for directory is not allowed. */
1300         if (ext4_inode_is_type(&mp->fs.sb, child_ref.inode,
1301                                EXT4_INODE_MODE_DIRECTORY)) {
1302                 r = EINVAL;
1303                 goto Finish;
1304         }
1305
1306         r = ext4_create_hardlink(hardlink_path, &child_ref, false);
1307
1308 Finish:
1309         if (child_loaded)
1310                 ext4_fs_put_inode_ref(&child_ref);
1311
1312         if (r != EOK)
1313                 ext4_trans_abort(mp);
1314         else
1315                 ext4_trans_stop(mp);
1316
1317         EXT4_MP_UNLOCK(mp);
1318         return r;
1319
1320 }
1321
1322 int ext4_frename(const char *path, const char *new_path)
1323 {
1324         int r;
1325         ext4_file f;
1326         uint32_t name_off;
1327         bool parent_loaded = false, child_loaded = false;
1328         uint32_t parent_inode, child_inode;
1329         struct ext4_mountpoint *mp = ext4_get_mount(path);
1330         struct ext4_inode_ref child_ref, parent_ref;
1331
1332         if (!mp)
1333                 return ENOENT;
1334
1335         if (mp->fs.read_only)
1336                 return EROFS;
1337
1338         EXT4_MP_LOCK(mp);
1339         ext4_trans_start(mp);
1340
1341         r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN,
1342                                 &parent_inode, &name_off);
1343         if (r != EOK)
1344                 goto Finish;
1345
1346         child_inode = f.inode;
1347         ext4_fclose(&f);
1348
1349         /*Load parent*/
1350         r = ext4_fs_get_inode_ref(&mp->fs, parent_inode, &parent_ref);
1351         if (r != EOK)
1352                 goto Finish;
1353
1354         parent_loaded = true;
1355
1356         /*We have file to unlink. Load it.*/
1357         r = ext4_fs_get_inode_ref(&mp->fs, child_inode, &child_ref);
1358         if (r != EOK)
1359                 goto Finish;
1360
1361         child_loaded = true;
1362
1363         r = ext4_create_hardlink(new_path, &child_ref, true);
1364         if (r != EOK)
1365                 goto Finish;
1366
1367         r = ext4_remove_orig_reference(path, name_off, &parent_ref, &child_ref);
1368         if (r != EOK)
1369                 goto Finish;
1370
1371 Finish:
1372         if (parent_loaded)
1373                 ext4_fs_put_inode_ref(&parent_ref);
1374
1375         if (child_loaded)
1376                 ext4_fs_put_inode_ref(&child_ref);
1377
1378         if (r != EOK)
1379                 ext4_trans_abort(mp);
1380         else
1381                 ext4_trans_stop(mp);
1382
1383         EXT4_MP_UNLOCK(mp);
1384         return r;
1385
1386 }
1387
1388 /****************************************************************************/
1389
1390 int ext4_get_sblock(const char *mount_point, struct ext4_sblock **sb)
1391 {
1392         struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
1393
1394         if (!mp)
1395                 return ENOENT;
1396
1397         *sb = &mp->fs.sb;
1398         return EOK;
1399 }
1400
1401 int ext4_cache_write_back(const char *path, bool on)
1402 {
1403         struct ext4_mountpoint *mp = ext4_get_mount(path);
1404         int ret;
1405
1406         if (!mp)
1407                 return ENOENT;
1408
1409         EXT4_MP_LOCK(mp);
1410         ret = ext4_block_cache_write_back(mp->fs.bdev, on);
1411         EXT4_MP_UNLOCK(mp);
1412         return ret;
1413 }
1414
1415 int ext4_cache_flush(const char *path)
1416 {
1417         struct ext4_mountpoint *mp = ext4_get_mount(path);
1418         int ret;
1419
1420         if (!mp)
1421                 return ENOENT;
1422
1423         EXT4_MP_LOCK(mp);
1424         ret = ext4_block_cache_flush(mp->fs.bdev);
1425         EXT4_MP_UNLOCK(mp);
1426         return ret;
1427 }
1428
1429 int ext4_fremove(const char *path)
1430 {
1431         ext4_file f;
1432         uint32_t parent_inode;
1433         uint32_t name_off;
1434         bool is_goal;
1435         int r;
1436         int len;
1437         struct ext4_inode_ref child;
1438         struct ext4_inode_ref parent;
1439         struct ext4_mountpoint *mp = ext4_get_mount(path);
1440
1441         if (!mp)
1442                 return ENOENT;
1443
1444         if (mp->fs.read_only)
1445                 return EROFS;
1446
1447         EXT4_MP_LOCK(mp);
1448         ext4_trans_start(mp);
1449
1450         r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DE_UNKNOWN,
1451                                &parent_inode, &name_off);
1452         if (r != EOK) {
1453                 ext4_trans_abort(mp);
1454                 EXT4_MP_UNLOCK(mp);
1455                 return r;
1456         }
1457
1458         /*Load parent*/
1459         r = ext4_fs_get_inode_ref(&mp->fs, parent_inode, &parent);
1460         if (r != EOK) {
1461                 ext4_trans_abort(mp);
1462                 EXT4_MP_UNLOCK(mp);
1463                 return r;
1464         }
1465
1466         /*We have file to delete. Load it.*/
1467         r = ext4_fs_get_inode_ref(&mp->fs, f.inode, &child);
1468         if (r != EOK) {
1469                 ext4_fs_put_inode_ref(&parent);
1470                 ext4_trans_abort(mp);
1471                 EXT4_MP_UNLOCK(mp);
1472                 return r;
1473         }
1474         /* We do not allow opening files here. */
1475         if (ext4_inode_type(&mp->fs.sb, child.inode) ==
1476             EXT4_INODE_MODE_DIRECTORY) {
1477                 ext4_fs_put_inode_ref(&parent);
1478                 ext4_fs_put_inode_ref(&child);
1479                 ext4_trans_abort(mp);
1480                 EXT4_MP_UNLOCK(mp);
1481                 return r;
1482         }
1483
1484         /*Link count will be zero, the inode should be freed. */
1485         if (ext4_inode_get_links_cnt(child.inode) == 1) {
1486                 ext4_block_cache_write_back(mp->fs.bdev, 1);
1487                 r = ext4_trunc_inode(mp, child.index, 0);
1488                 if (r != EOK) {
1489                         ext4_fs_put_inode_ref(&parent);
1490                         ext4_fs_put_inode_ref(&child);
1491                         ext4_trans_abort(mp);
1492                         EXT4_MP_UNLOCK(mp);
1493                         return r;
1494                 }
1495                 ext4_block_cache_write_back(mp->fs.bdev, 0);
1496         }
1497
1498         /*Set path*/
1499         path += name_off;
1500
1501         len = ext4_path_check(path, &is_goal);
1502
1503         /*Unlink from parent*/
1504         r = ext4_unlink(mp, &parent, &child, path, len);
1505         if (r != EOK)
1506                 goto Finish;
1507
1508         /*Link count is zero, the inode should be freed. */
1509         if (!ext4_inode_get_links_cnt(child.inode)) {
1510                 ext4_inode_set_del_time(child.inode, -1L);
1511
1512                 r = ext4_fs_free_inode(&child);
1513                 if (r != EOK)
1514                         goto Finish;
1515         }
1516
1517 Finish:
1518         ext4_fs_put_inode_ref(&child);
1519         ext4_fs_put_inode_ref(&parent);
1520
1521         if (r != EOK)
1522                 ext4_trans_abort(mp);
1523         else
1524                 ext4_trans_stop(mp);
1525
1526         EXT4_MP_UNLOCK(mp);
1527         return r;
1528 }
1529
1530 int ext4_fill_raw_inode(const char *path, uint32_t *ret_ino,
1531                         struct ext4_inode *inode)
1532 {
1533         int r;
1534         ext4_file f;
1535         struct ext4_inode_ref inode_ref;
1536         struct ext4_mountpoint *mp = ext4_get_mount(path);
1537         uint32_t ino;
1538
1539         if (!mp)
1540                 return ENOENT;
1541
1542         EXT4_MP_LOCK(mp);
1543
1544         r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, NULL, NULL);
1545         if (r != EOK) {
1546                 EXT4_MP_UNLOCK(mp);
1547                 return r;
1548         }
1549
1550         ino = f.inode;
1551         ext4_fclose(&f);
1552
1553         /*Load parent*/
1554         r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
1555         if (r != EOK) {
1556                 EXT4_MP_UNLOCK(mp);
1557                 return r;
1558         }
1559
1560         memcpy(inode, inode_ref.inode, sizeof(struct ext4_inode));
1561         ext4_fs_put_inode_ref(&inode_ref);
1562         EXT4_MP_UNLOCK(mp);
1563
1564         if (ret_ino)
1565                 *ret_ino = ino;
1566
1567         return r;
1568 }
1569
1570 int ext4_fopen(ext4_file *f, const char *path, const char *flags)
1571 {
1572         struct ext4_mountpoint *mp = ext4_get_mount(path);
1573         int r;
1574
1575         if (!mp)
1576                 return ENOENT;
1577
1578         EXT4_MP_LOCK(mp);
1579
1580         ext4_block_cache_write_back(mp->fs.bdev, 1);
1581         r = ext4_generic_open(f, path, flags, true, 0, 0);
1582         ext4_block_cache_write_back(mp->fs.bdev, 0);
1583
1584         EXT4_MP_UNLOCK(mp);
1585         return r;
1586 }
1587
1588 int ext4_fopen2(ext4_file *f, const char *path, int flags)
1589 {
1590         struct ext4_mountpoint *mp = ext4_get_mount(path);
1591         int r;
1592         int filetype;
1593
1594         if (!mp)
1595                 return ENOENT;
1596
1597         filetype = EXT4_DE_REG_FILE;
1598
1599         EXT4_MP_LOCK(mp);
1600
1601         ext4_block_cache_write_back(mp->fs.bdev, 1);
1602         r = ext4_generic_open2(f, path, flags, filetype, NULL, NULL);
1603         ext4_block_cache_write_back(mp->fs.bdev, 0);
1604
1605         EXT4_MP_UNLOCK(mp);
1606         return r;
1607 }
1608
1609 int ext4_fclose(ext4_file *f)
1610 {
1611         ext4_assert(f && f->mp);
1612
1613         f->mp = 0;
1614         f->flags = 0;
1615         f->inode = 0;
1616         f->fpos = f->fsize = 0;
1617
1618         return EOK;
1619 }
1620
1621 static int ext4_ftruncate_no_lock(ext4_file *f, uint64_t size)
1622 {
1623         struct ext4_inode_ref ref;
1624         int r;
1625
1626
1627         r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
1628         if (r != EOK) {
1629                 EXT4_MP_UNLOCK(f->mp);
1630                 return r;
1631         }
1632
1633         /*Sync file size*/
1634         f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);
1635         if (f->fsize <= size) {
1636                 r = EOK;
1637                 goto Finish;
1638         }
1639
1640         /*Start write back cache mode.*/
1641         r = ext4_block_cache_write_back(f->mp->fs.bdev, 1);
1642         if (r != EOK)
1643                 goto Finish;
1644
1645         r = ext4_trunc_inode(f->mp, ref.index, size);
1646         if (r != EOK)
1647                 goto Finish;
1648
1649         f->fsize = size;
1650         if (f->fpos > size)
1651                 f->fpos = size;
1652
1653         /*Stop write back cache mode*/
1654         ext4_block_cache_write_back(f->mp->fs.bdev, 0);
1655
1656         if (r != EOK)
1657                 goto Finish;
1658
1659 Finish:
1660         ext4_fs_put_inode_ref(&ref);
1661         return r;
1662
1663 }
1664
1665 int ext4_ftruncate(ext4_file *f, uint64_t size)
1666 {
1667         int r;
1668         ext4_assert(f && f->mp);
1669
1670         if (f->mp->fs.read_only)
1671                 return EROFS;
1672
1673         if (f->flags & O_RDONLY)
1674                 return EPERM;
1675
1676         EXT4_MP_LOCK(f->mp);
1677
1678         ext4_trans_start(f->mp);
1679         r = ext4_ftruncate_no_lock(f, size);
1680         if (r != EOK)
1681                 ext4_trans_abort(f->mp);
1682         else
1683                 ext4_trans_stop(f->mp);
1684
1685         EXT4_MP_UNLOCK(f->mp);
1686         return r;
1687 }
1688
1689 int ext4_fread(ext4_file *f, void *buf, size_t size, size_t *rcnt)
1690 {
1691         uint32_t unalg;
1692         uint32_t iblock_idx;
1693         uint32_t iblock_last;
1694         uint32_t block_size;
1695
1696         ext4_fsblk_t fblock;
1697         ext4_fsblk_t fblock_start;
1698         uint32_t fblock_count;
1699
1700         uint8_t *u8_buf = buf;
1701         int r;
1702         struct ext4_inode_ref ref;
1703
1704         ext4_assert(f && f->mp);
1705
1706         if (f->flags & O_WRONLY)
1707                 return EPERM;
1708
1709         if (!size)
1710                 return EOK;
1711
1712         EXT4_MP_LOCK(f->mp);
1713
1714         struct ext4_fs *const fs = &f->mp->fs;
1715         struct ext4_sblock *const sb = &f->mp->fs.sb;
1716
1717         if (rcnt)
1718                 *rcnt = 0;
1719
1720         r = ext4_fs_get_inode_ref(fs, f->inode, &ref);
1721         if (r != EOK) {
1722                 EXT4_MP_UNLOCK(f->mp);
1723                 return r;
1724         }
1725
1726         /*Sync file size*/
1727         f->fsize = ext4_inode_get_size(sb, ref.inode);
1728
1729         block_size = ext4_sb_get_block_size(sb);
1730         size = ((uint64_t)size > (f->fsize - f->fpos))
1731                 ? ((size_t)(f->fsize - f->fpos)) : size;
1732
1733         iblock_idx = (uint32_t)((f->fpos) / block_size);
1734         iblock_last = (uint32_t)((f->fpos + size) / block_size);
1735         unalg = (f->fpos) % block_size;
1736
1737         /*If the size of symlink is smaller than 60 bytes*/
1738         bool softlink;
1739         softlink = ext4_inode_is_type(sb, ref.inode, EXT4_INODE_MODE_SOFTLINK);
1740         if (softlink && f->fsize < sizeof(ref.inode->blocks)
1741                      && !ext4_inode_get_blocks_count(sb, ref.inode)) {
1742
1743                 char *content = (char *)ref.inode->blocks;
1744                 if (f->fpos < f->fsize) {
1745                         size_t len = size;
1746                         if (unalg + size > (uint32_t)f->fsize)
1747                                 len = (uint32_t)f->fsize - unalg;
1748                         memcpy(buf, content + unalg, len);
1749                         if (rcnt)
1750                                 *rcnt = len;
1751
1752                 }
1753
1754                 r = EOK;
1755                 goto Finish;
1756         }
1757
1758         if (unalg) {
1759                 size_t len =  size;
1760                 if (size > (block_size - unalg))
1761                         len = block_size - unalg;
1762
1763                 r = ext4_fs_get_inode_dblk_idx(&ref, iblock_idx, &fblock, true);
1764                 if (r != EOK)
1765                         goto Finish;
1766
1767                 /* Do we get an unwritten range? */
1768                 if (fblock != 0) {
1769                         uint64_t off = fblock * block_size + unalg;
1770                         r = ext4_block_readbytes(f->mp->fs.bdev, off, u8_buf, len);
1771                         if (r != EOK)
1772                                 goto Finish;
1773
1774                 } else {
1775                         /* Yes, we do. */
1776                         memset(u8_buf, 0, len);
1777                 }
1778
1779                 u8_buf += len;
1780                 size -= len;
1781                 f->fpos += len;
1782
1783                 if (rcnt)
1784                         *rcnt += len;
1785
1786                 iblock_idx++;
1787         }
1788
1789         fblock_start = 0;
1790         fblock_count = 0;
1791         while (size >= block_size) {
1792                 while (iblock_idx < iblock_last) {
1793                         r = ext4_fs_get_inode_dblk_idx(&ref, iblock_idx,
1794                                                        &fblock, true);
1795                         if (r != EOK)
1796                                 goto Finish;
1797
1798                         iblock_idx++;
1799
1800                         if (!fblock_start)
1801                                 fblock_start = fblock;
1802
1803                         if ((fblock_start + fblock_count) != fblock)
1804                                 break;
1805
1806                         fblock_count++;
1807                 }
1808
1809                 r = ext4_blocks_get_direct(f->mp->fs.bdev, u8_buf, fblock_start,
1810                                            fblock_count);
1811                 if (r != EOK)
1812                         goto Finish;
1813
1814                 size -= block_size * fblock_count;
1815                 u8_buf += block_size * fblock_count;
1816                 f->fpos += block_size * fblock_count;
1817
1818                 if (rcnt)
1819                         *rcnt += block_size * fblock_count;
1820
1821                 fblock_start = fblock;
1822                 fblock_count = 1;
1823         }
1824
1825         if (size) {
1826                 uint64_t off;
1827                 r = ext4_fs_get_inode_dblk_idx(&ref, iblock_idx, &fblock, true);
1828                 if (r != EOK)
1829                         goto Finish;
1830
1831                 off = fblock * block_size;
1832                 r = ext4_block_readbytes(f->mp->fs.bdev, off, u8_buf, size);
1833                 if (r != EOK)
1834                         goto Finish;
1835
1836                 f->fpos += size;
1837
1838                 if (rcnt)
1839                         *rcnt += size;
1840         }
1841
1842 Finish:
1843         ext4_fs_put_inode_ref(&ref);
1844         EXT4_MP_UNLOCK(f->mp);
1845         return r;
1846 }
1847
1848 int ext4_fwrite(ext4_file *f, const void *buf, size_t size, size_t *wcnt)
1849 {
1850         uint32_t unalg;
1851         uint32_t iblk_idx;
1852         uint32_t iblock_last;
1853         uint32_t ifile_blocks;
1854         uint32_t block_size;
1855
1856         uint32_t fblock_count;
1857         ext4_fsblk_t fblk;
1858         ext4_fsblk_t fblock_start;
1859
1860         struct ext4_inode_ref ref;
1861         const uint8_t *u8_buf = buf;
1862         int r, rr = EOK;
1863
1864         ext4_assert(f && f->mp);
1865
1866         if (f->mp->fs.read_only)
1867                 return EROFS;
1868
1869         if (f->flags & O_RDONLY)
1870                 return EPERM;
1871
1872         if (!size)
1873                 return EOK;
1874
1875         EXT4_MP_LOCK(f->mp);
1876         ext4_trans_start(f->mp);
1877
1878         struct ext4_fs *const fs = &f->mp->fs;
1879         struct ext4_sblock *const sb = &f->mp->fs.sb;
1880
1881         if (wcnt)
1882                 *wcnt = 0;
1883
1884         r = ext4_fs_get_inode_ref(fs, f->inode, &ref);
1885         if (r != EOK) {
1886                 ext4_trans_abort(f->mp);
1887                 EXT4_MP_UNLOCK(f->mp);
1888                 return r;
1889         }
1890
1891         /*Sync file size*/
1892         f->fsize = ext4_inode_get_size(sb, ref.inode);
1893         block_size = ext4_sb_get_block_size(sb);
1894
1895         iblock_last = (uint32_t)((f->fpos + size) / block_size);
1896         iblk_idx = (uint32_t)(f->fpos / block_size);
1897         ifile_blocks = (uint32_t)((f->fsize + block_size - 1) / block_size);
1898
1899         unalg = (f->fpos) % block_size;
1900
1901         if (unalg) {
1902                 size_t len =  size;
1903                 uint64_t off;
1904                 if (size > (block_size - unalg))
1905                         len = block_size - unalg;
1906
1907                 r = ext4_fs_init_inode_dblk_idx(&ref, iblk_idx, &fblk);
1908                 if (r != EOK)
1909                         goto Finish;
1910
1911                 off = fblk * block_size + unalg;
1912                 r = ext4_block_writebytes(f->mp->fs.bdev, off, u8_buf, len);
1913                 if (r != EOK)
1914                         goto Finish;
1915
1916                 u8_buf += len;
1917                 size -= len;
1918                 f->fpos += len;
1919
1920                 if (wcnt)
1921                         *wcnt += len;
1922
1923                 iblk_idx++;
1924         }
1925
1926         /*Start write back cache mode.*/
1927         r = ext4_block_cache_write_back(f->mp->fs.bdev, 1);
1928         if (r != EOK)
1929                 goto Finish;
1930
1931         fblock_start = 0;
1932         fblock_count = 0;
1933         while (size >= block_size) {
1934
1935                 while (iblk_idx < iblock_last) {
1936                         if (iblk_idx < ifile_blocks) {
1937                                 r = ext4_fs_init_inode_dblk_idx(&ref, iblk_idx,
1938                                                                 &fblk);
1939                                 if (r != EOK)
1940                                         goto Finish;
1941                         } else {
1942                                 rr = ext4_fs_append_inode_dblk(&ref, &fblk,
1943                                                                &iblk_idx);
1944                                 if (rr != EOK) {
1945                                         /* Unable to append more blocks. But
1946                                          * some block might be allocated already
1947                                          * */
1948                                         break;
1949                                 }
1950                         }
1951
1952                         iblk_idx++;
1953
1954                         if (!fblock_start) {
1955                                 fblock_start = fblk;
1956                         }
1957
1958                         if ((fblock_start + fblock_count) != fblk)
1959                                 break;
1960
1961                         fblock_count++;
1962                 }
1963
1964                 r = ext4_blocks_set_direct(f->mp->fs.bdev, u8_buf, fblock_start,
1965                                            fblock_count);
1966                 if (r != EOK)
1967                         break;
1968
1969                 size -= block_size * fblock_count;
1970                 u8_buf += block_size * fblock_count;
1971                 f->fpos += block_size * fblock_count;
1972
1973                 if (wcnt)
1974                         *wcnt += block_size * fblock_count;
1975
1976                 fblock_start = fblk;
1977                 fblock_count = 1;
1978
1979                 if (rr != EOK) {
1980                         /*ext4_fs_append_inode_block has failed and no
1981                          * more blocks might be written. But node size
1982                          * should be updated.*/
1983                         r = rr;
1984                         goto out_fsize;
1985                 }
1986         }
1987
1988         /*Stop write back cache mode*/
1989         ext4_block_cache_write_back(f->mp->fs.bdev, 0);
1990
1991         if (r != EOK)
1992                 goto Finish;
1993
1994         if (size) {
1995                 uint64_t off;
1996                 if (iblk_idx < ifile_blocks) {
1997                         r = ext4_fs_init_inode_dblk_idx(&ref, iblk_idx, &fblk);
1998                         if (r != EOK)
1999                                 goto Finish;
2000                 } else {
2001                         r = ext4_fs_append_inode_dblk(&ref, &fblk, &iblk_idx);
2002                         if (r != EOK)
2003                                 /*Node size sholud be updated.*/
2004                                 goto out_fsize;
2005                 }
2006
2007                 off = fblk * block_size;
2008                 r = ext4_block_writebytes(f->mp->fs.bdev, off, u8_buf, size);
2009                 if (r != EOK)
2010                         goto Finish;
2011
2012                 f->fpos += size;
2013
2014                 if (wcnt)
2015                         *wcnt += size;
2016         }
2017
2018 out_fsize:
2019         if (f->fpos > f->fsize) {
2020                 f->fsize = f->fpos;
2021                 ext4_inode_set_size(ref.inode, f->fsize);
2022                 ref.dirty = true;
2023         }
2024
2025 Finish:
2026         r = ext4_fs_put_inode_ref(&ref);
2027
2028         if (r != EOK)
2029                 ext4_trans_abort(f->mp);
2030         else
2031                 ext4_trans_stop(f->mp);
2032
2033         EXT4_MP_UNLOCK(f->mp);
2034         return r;
2035 }
2036
2037 int ext4_fseek(ext4_file *f, uint64_t offset, uint32_t origin)
2038 {
2039         switch (origin) {
2040         case SEEK_SET:
2041                 if (offset > f->fsize)
2042                         return EINVAL;
2043
2044                 f->fpos = offset;
2045                 return EOK;
2046         case SEEK_CUR:
2047                 if ((offset + f->fpos) > f->fsize)
2048                         return EINVAL;
2049
2050                 f->fpos += offset;
2051                 return EOK;
2052         case SEEK_END:
2053                 if (offset > f->fsize)
2054                         return EINVAL;
2055
2056                 f->fpos = f->fsize - offset;
2057                 return EOK;
2058         }
2059         return EINVAL;
2060 }
2061
2062 uint64_t ext4_ftell(ext4_file *f)
2063 {
2064         return f->fpos;
2065 }
2066
2067 uint64_t ext4_fsize(ext4_file *f)
2068 {
2069         return f->fsize;
2070 }
2071
2072
2073 static int ext4_trans_get_inode_ref(const char *path,
2074                                     struct ext4_mountpoint *mp,
2075                                     struct ext4_inode_ref *inode_ref)
2076 {
2077         int r;
2078         ext4_file f;
2079
2080         ext4_trans_start(mp);
2081         r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DE_UNKNOWN, NULL, NULL);
2082         if (r != EOK) {
2083                 ext4_trans_abort(mp);
2084                 return r;
2085         }
2086
2087         r = ext4_fs_get_inode_ref(&mp->fs, f.inode, inode_ref);
2088         if (r != EOK) {
2089                 ext4_trans_abort(mp);
2090                 return r;
2091         }
2092
2093         return r;
2094 }
2095
2096 static int ext4_trans_put_inode_ref(struct ext4_mountpoint *mp,
2097                                     struct ext4_inode_ref *inode_ref)
2098 {
2099         int r;
2100
2101         r = ext4_fs_put_inode_ref(inode_ref);
2102         if (r != EOK)
2103                 ext4_trans_abort(mp);
2104         else
2105                 ext4_trans_stop(mp);
2106
2107         return r;
2108 }
2109
2110 int ext4_mode_set(const char *path, uint32_t mode)
2111 {
2112         int r;
2113         uint32_t orig_mode;
2114         struct ext4_inode_ref inode_ref;
2115         struct ext4_mountpoint *mp = ext4_get_mount(path);
2116
2117         if (!mp)
2118                 return ENOENT;
2119
2120         if (mp->fs.read_only)
2121                 return EROFS;
2122
2123         EXT4_MP_LOCK(mp);
2124
2125         r = ext4_trans_get_inode_ref(path, mp, &inode_ref);
2126         if (r != EOK)
2127                 goto Finish;
2128
2129         orig_mode = ext4_inode_get_mode(&mp->fs.sb, inode_ref.inode);
2130         orig_mode &= ~0xFFF;
2131         orig_mode |= mode & 0xFFF;
2132         ext4_inode_set_mode(&mp->fs.sb, inode_ref.inode, orig_mode);
2133
2134         inode_ref.dirty = true;
2135         r = ext4_trans_put_inode_ref(mp, &inode_ref);
2136
2137         Finish:
2138         EXT4_MP_UNLOCK(mp);
2139
2140         return r;
2141 }
2142
2143 int ext4_owner_set(const char *path, uint32_t uid, uint32_t gid)
2144 {
2145         int r;
2146         struct ext4_inode_ref inode_ref;
2147         struct ext4_mountpoint *mp = ext4_get_mount(path);
2148
2149         if (!mp)
2150                 return ENOENT;
2151
2152         if (mp->fs.read_only)
2153                 return EROFS;
2154
2155         EXT4_MP_LOCK(mp);
2156
2157         r = ext4_trans_get_inode_ref(path, mp, &inode_ref);
2158         if (r != EOK)
2159                 goto Finish;
2160
2161         ext4_inode_set_uid(inode_ref.inode, uid);
2162         ext4_inode_set_gid(inode_ref.inode, gid);
2163
2164         inode_ref.dirty = true;
2165         r = ext4_trans_put_inode_ref(mp, &inode_ref);
2166
2167         Finish:
2168         EXT4_MP_UNLOCK(mp);
2169
2170         return r;
2171 }
2172
2173 int ext4_mode_get(const char *path, uint32_t *mode)
2174 {
2175         struct ext4_inode_ref inode_ref;
2176         struct ext4_mountpoint *mp = ext4_get_mount(path);
2177         ext4_file f;
2178         int r;
2179
2180         if (!mp)
2181                 return ENOENT;
2182
2183         EXT4_MP_LOCK(mp);
2184
2185         r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, NULL, NULL);
2186         if (r != EOK)
2187                 goto Finish;
2188
2189         r = ext4_fs_get_inode_ref(&mp->fs, f.inode, &inode_ref);
2190         if (r != EOK)
2191                 goto Finish;
2192
2193         *mode = ext4_inode_get_mode(&mp->fs.sb, inode_ref.inode);
2194         r = ext4_fs_put_inode_ref(&inode_ref);
2195
2196         Finish:
2197         EXT4_MP_UNLOCK(mp);
2198
2199         return r;
2200 }
2201
2202 int ext4_owner_get(const char *path, uint32_t *uid, uint32_t *gid)
2203 {
2204         struct ext4_inode_ref inode_ref;
2205         struct ext4_mountpoint *mp = ext4_get_mount(path);
2206         ext4_file f;
2207         int r;
2208
2209         if (!mp)
2210                 return ENOENT;
2211
2212         EXT4_MP_LOCK(mp);
2213
2214         r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, NULL, NULL);
2215         if (r != EOK)
2216                 goto Finish;
2217
2218         r = ext4_fs_get_inode_ref(&mp->fs, f.inode, &inode_ref);
2219         if (r != EOK)
2220                 goto Finish;
2221
2222         *uid = ext4_inode_get_uid(inode_ref.inode);
2223         *gid = ext4_inode_get_gid(inode_ref.inode);
2224         r = ext4_fs_put_inode_ref(&inode_ref);
2225
2226         Finish:
2227         EXT4_MP_UNLOCK(mp);
2228
2229         return r;
2230 }
2231
2232 int ext4_atime_set(const char *path, uint32_t atime)
2233 {
2234         struct ext4_inode_ref inode_ref;
2235         struct ext4_mountpoint *mp = ext4_get_mount(path);
2236         int r;
2237
2238         if (!mp)
2239                 return ENOENT;
2240
2241         if (mp->fs.read_only)
2242                 return EROFS;
2243
2244         EXT4_MP_LOCK(mp);
2245
2246         r = ext4_trans_get_inode_ref(path, mp, &inode_ref);
2247         if (r != EOK)
2248                 goto Finish;
2249
2250         ext4_inode_set_access_time(inode_ref.inode, atime);
2251         inode_ref.dirty = true;
2252         r = ext4_trans_put_inode_ref(mp, &inode_ref);
2253
2254         Finish:
2255         EXT4_MP_UNLOCK(mp);
2256
2257         return r;
2258 }
2259
2260 int ext4_mtime_set(const char *path, uint32_t mtime)
2261 {
2262         struct ext4_inode_ref inode_ref;
2263         struct ext4_mountpoint *mp = ext4_get_mount(path);
2264         int r;
2265
2266         if (!mp)
2267                 return ENOENT;
2268
2269         if (mp->fs.read_only)
2270                 return EROFS;
2271
2272         EXT4_MP_LOCK(mp);
2273
2274         r = ext4_trans_get_inode_ref(path, mp, &inode_ref);
2275         if (r != EOK)
2276                 goto Finish;
2277
2278         ext4_inode_set_modif_time(inode_ref.inode, mtime);
2279         inode_ref.dirty = true;
2280         r = ext4_trans_put_inode_ref(mp, &inode_ref);
2281
2282         Finish:
2283         EXT4_MP_UNLOCK(mp);
2284
2285         return r;
2286 }
2287
2288 int ext4_ctime_set(const char *path, uint32_t ctime)
2289 {
2290         struct ext4_inode_ref inode_ref;
2291         struct ext4_mountpoint *mp = ext4_get_mount(path);
2292         int r;
2293
2294         if (!mp)
2295                 return ENOENT;
2296
2297         if (mp->fs.read_only)
2298                 return EROFS;
2299
2300         EXT4_MP_LOCK(mp);
2301
2302         r = ext4_trans_get_inode_ref(path, mp, &inode_ref);
2303         if (r != EOK)
2304                 goto Finish;
2305
2306         ext4_inode_set_change_inode_time(inode_ref.inode, ctime);
2307         inode_ref.dirty = true;
2308         r = ext4_trans_put_inode_ref(mp, &inode_ref);
2309
2310         Finish:
2311         EXT4_MP_UNLOCK(mp);
2312
2313         return r;
2314 }
2315
2316 int ext4_atime_get(const char *path, uint32_t *atime)
2317 {
2318         struct ext4_inode_ref inode_ref;
2319         struct ext4_mountpoint *mp = ext4_get_mount(path);
2320         ext4_file f;
2321         int r;
2322
2323         if (!mp)
2324                 return ENOENT;
2325
2326         EXT4_MP_LOCK(mp);
2327
2328         r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, NULL, NULL);
2329         if (r != EOK)
2330                 goto Finish;
2331
2332         r = ext4_fs_get_inode_ref(&mp->fs, f.inode, &inode_ref);
2333         if (r != EOK)
2334                 goto Finish;
2335
2336         *atime = ext4_inode_get_access_time(inode_ref.inode);
2337         r = ext4_fs_put_inode_ref(&inode_ref);
2338
2339         Finish:
2340         EXT4_MP_UNLOCK(mp);
2341
2342         return r;
2343 }
2344
2345 int ext4_mtime_get(const char *path, uint32_t *mtime)
2346 {
2347         struct ext4_inode_ref inode_ref;
2348         struct ext4_mountpoint *mp = ext4_get_mount(path);
2349         ext4_file f;
2350         int r;
2351
2352         if (!mp)
2353                 return ENOENT;
2354
2355         EXT4_MP_LOCK(mp);
2356
2357         r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, NULL, NULL);
2358         if (r != EOK)
2359                 goto Finish;
2360
2361         r = ext4_fs_get_inode_ref(&mp->fs, f.inode, &inode_ref);
2362         if (r != EOK)
2363                 goto Finish;
2364
2365         *mtime = ext4_inode_get_modif_time(inode_ref.inode);
2366         r = ext4_fs_put_inode_ref(&inode_ref);
2367
2368         Finish:
2369         EXT4_MP_UNLOCK(mp);
2370
2371         return r;
2372 }
2373
2374 int ext4_ctime_get(const char *path, uint32_t *ctime)
2375 {
2376         struct ext4_inode_ref inode_ref;
2377         struct ext4_mountpoint *mp = ext4_get_mount(path);
2378         ext4_file f;
2379         int r;
2380
2381         if (!mp)
2382                 return ENOENT;
2383
2384         EXT4_MP_LOCK(mp);
2385
2386         r = ext4_generic_open2(&f, path, O_RDONLY, EXT4_DE_UNKNOWN, NULL, NULL);
2387         if (r != EOK)
2388                 goto Finish;
2389
2390         r = ext4_fs_get_inode_ref(&mp->fs, f.inode, &inode_ref);
2391         if (r != EOK)
2392                 goto Finish;
2393
2394         *ctime = ext4_inode_get_change_inode_time(inode_ref.inode);
2395         r = ext4_fs_put_inode_ref(&inode_ref);
2396
2397         Finish:
2398         EXT4_MP_UNLOCK(mp);
2399
2400         return r;
2401 }
2402
2403 static int ext4_fsymlink_set(ext4_file *f, const void *buf, uint32_t size)
2404 {
2405         struct ext4_inode_ref ref;
2406         uint32_t sblock;
2407         ext4_fsblk_t fblock;
2408         uint32_t block_size;
2409         int r;
2410
2411         ext4_assert(f && f->mp);
2412
2413         if (!size)
2414                 return EOK;
2415
2416         r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
2417         if (r != EOK)
2418                 return r;
2419
2420         /*Sync file size*/
2421         block_size = ext4_sb_get_block_size(&f->mp->fs.sb);
2422         if (size > block_size) {
2423                 r = EINVAL;
2424                 goto Finish;
2425         }
2426         r = ext4_ftruncate_no_lock(f, 0);
2427         if (r != EOK)
2428                 goto Finish;
2429
2430         /*Start write back cache mode.*/
2431         r = ext4_block_cache_write_back(f->mp->fs.bdev, 1);
2432         if (r != EOK)
2433                 goto Finish;
2434
2435         /*If the size of symlink is smaller than 60 bytes*/
2436         if (size < sizeof(ref.inode->blocks)) {
2437                 memset(ref.inode->blocks, 0, sizeof(ref.inode->blocks));
2438                 memcpy(ref.inode->blocks, buf, size);
2439                 ext4_inode_clear_flag(ref.inode, EXT4_INODE_FLAG_EXTENTS);
2440         } else {
2441                 ext4_fs_inode_blocks_init(&f->mp->fs, &ref);
2442                 r = ext4_fs_append_inode_dblk(&ref, &fblock, &sblock);
2443                 if (r != EOK)
2444                         goto Finish;
2445
2446                 r = ext4_block_writebytes(f->mp->fs.bdev, 0, buf, size);
2447                 if (r != EOK)
2448                         goto Finish;
2449
2450         }
2451
2452         /*Stop write back cache mode*/
2453         ext4_block_cache_write_back(f->mp->fs.bdev, 0);
2454
2455         if (r != EOK)
2456                 goto Finish;
2457
2458         ext4_inode_set_size(ref.inode, size);
2459         ref.dirty = true;
2460
2461         f->fsize = size;
2462         if (f->fpos > size)
2463                 f->fpos = size;
2464
2465 Finish:
2466         ext4_fs_put_inode_ref(&ref);
2467         return r;
2468 }
2469
2470 int ext4_fsymlink(const char *target, const char *path)
2471 {
2472         struct ext4_mountpoint *mp = ext4_get_mount(path);
2473         int r;
2474         ext4_file f;
2475         int filetype;
2476
2477         if (!mp)
2478                 return ENOENT;
2479
2480         if (mp->fs.read_only)
2481                 return EROFS;
2482
2483         filetype = EXT4_DE_SYMLINK;
2484
2485         EXT4_MP_LOCK(mp);
2486         ext4_trans_start(mp);
2487
2488         ext4_block_cache_write_back(mp->fs.bdev, 1);
2489         r = ext4_generic_open2(&f, path, O_RDWR|O_CREAT, filetype, NULL, NULL);
2490         if (r == EOK)
2491                 r = ext4_fsymlink_set(&f, target, strlen(target));
2492         else
2493                 goto Finish;
2494
2495         ext4_fclose(&f);
2496
2497 Finish:
2498         ext4_block_cache_write_back(mp->fs.bdev, 0);
2499
2500         if (r != EOK)
2501                 ext4_trans_abort(mp);
2502         else
2503                 ext4_trans_stop(mp);
2504
2505         EXT4_MP_UNLOCK(mp);
2506         return r;
2507 }
2508
2509 int ext4_readlink(const char *path, char *buf, size_t bufsize, size_t *rcnt)
2510 {
2511         struct ext4_mountpoint *mp = ext4_get_mount(path);
2512         int r;
2513         ext4_file f;
2514         int filetype;
2515
2516         if (!mp)
2517                 return ENOENT;
2518
2519         if (!buf)
2520                 return EINVAL;
2521
2522         filetype = EXT4_DE_SYMLINK;
2523
2524         EXT4_MP_LOCK(mp);
2525         ext4_block_cache_write_back(mp->fs.bdev, 1);
2526         r = ext4_generic_open2(&f, path, O_RDONLY, filetype, NULL, NULL);
2527         if (r == EOK)
2528                 r = ext4_fread(&f, buf, bufsize, rcnt);
2529         else
2530                 goto Finish;
2531
2532         ext4_fclose(&f);
2533
2534 Finish:
2535         ext4_block_cache_write_back(mp->fs.bdev, 0);
2536         if (r != EOK)
2537                 ext4_trans_abort(mp);
2538         else
2539                 ext4_trans_stop(mp);
2540
2541         EXT4_MP_UNLOCK(mp);
2542         return r;
2543 }
2544
2545 static int ext4_mknod_set(ext4_file *f, uint32_t dev)
2546 {
2547         struct ext4_inode_ref ref;
2548         int r;
2549
2550         ext4_assert(f && f->mp);
2551
2552         r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
2553         if (r != EOK)
2554                 return r;
2555
2556         ext4_inode_set_dev(ref.inode, dev);
2557
2558         ext4_inode_set_size(ref.inode, 0);
2559         ref.dirty = true;
2560
2561         f->fsize = 0;
2562         f->fpos = 0;
2563
2564         r = ext4_fs_put_inode_ref(&ref);
2565         return r;
2566 }
2567
2568 int ext4_mknod(const char *path, int filetype, uint32_t dev)
2569 {
2570         struct ext4_mountpoint *mp = ext4_get_mount(path);
2571         int r;
2572         ext4_file f;
2573
2574         if (!mp)
2575                 return ENOENT;
2576
2577         if (mp->fs.read_only)
2578                 return EROFS;
2579
2580         /*
2581          * The filetype shouldn't be normal file, directory or
2582          * unknown.
2583          */
2584         if (filetype == EXT4_DE_UNKNOWN ||
2585             filetype == EXT4_DE_REG_FILE ||
2586             filetype == EXT4_DE_DIR ||
2587             filetype == EXT4_DE_SYMLINK)
2588                 return EINVAL;
2589
2590         /*
2591          * Nor should it be any bogus value.
2592          */
2593         if (filetype != EXT4_DE_CHRDEV &&
2594             filetype != EXT4_DE_BLKDEV &&
2595             filetype != EXT4_DE_FIFO &&
2596             filetype != EXT4_DE_SOCK)
2597                 return EINVAL;
2598
2599         EXT4_MP_LOCK(mp);
2600         ext4_trans_start(mp);
2601
2602         ext4_block_cache_write_back(mp->fs.bdev, 1);
2603         r = ext4_generic_open2(&f, path, O_RDWR|O_CREAT, filetype, NULL, NULL);
2604         if (r == EOK) {
2605                 if (filetype == EXT4_DE_CHRDEV ||
2606                     filetype == EXT4_DE_BLKDEV)
2607                         r = ext4_mknod_set(&f, dev);
2608         } else
2609                 goto Finish;
2610
2611         ext4_fclose(&f);
2612
2613 Finish:
2614         ext4_block_cache_write_back(mp->fs.bdev, 0);
2615
2616         if (r != EOK)
2617                 ext4_trans_abort(mp);
2618         else
2619                 ext4_trans_stop(mp);
2620
2621         EXT4_MP_UNLOCK(mp);
2622         return r;
2623 }
2624
2625 int ext4_setxattr(const char *path, const char *name, size_t name_len,
2626                   const void *data, size_t data_size, bool replace __unused)
2627 {
2628         bool found;
2629         int r = EOK;
2630         ext4_file f;
2631         uint32_t inode;
2632         uint8_t name_index;
2633         const char *dissected_name = NULL;
2634         size_t dissected_len = 0;
2635         struct ext4_inode_ref inode_ref;
2636         struct ext4_mountpoint *mp = ext4_get_mount(path);
2637         if (!mp)
2638                 return ENOENT;
2639
2640         if (mp->fs.read_only)
2641                 return EROFS;
2642
2643         dissected_name = ext4_extract_xattr_name(name, name_len,
2644                                 &name_index, &dissected_len,
2645                                 &found);
2646         if (!found)
2647                 return EINVAL;
2648
2649         EXT4_MP_LOCK(mp);
2650         ext4_trans_start(mp);
2651
2652         r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DE_UNKNOWN, NULL, NULL);
2653         if (r != EOK)
2654                 goto Finish;
2655         inode = f.inode;
2656         ext4_fclose(&f);
2657
2658         r = ext4_fs_get_inode_ref(&mp->fs, inode, &inode_ref);
2659         if (r != EOK)
2660                 goto Finish;
2661
2662         r = ext4_xattr_set(&inode_ref, name_index, dissected_name,
2663                         dissected_len, data, data_size);
2664
2665         ext4_fs_put_inode_ref(&inode_ref);
2666 Finish:
2667         if (r != EOK)
2668                 ext4_trans_abort(mp);
2669         else
2670                 ext4_trans_stop(mp);
2671
2672         EXT4_MP_UNLOCK(mp);
2673         return r;
2674 }
2675
2676 int ext4_getxattr(const char *path, const char *name, size_t name_len,
2677                   void *buf, size_t buf_size, size_t *data_size)
2678 {
2679         bool found;
2680         int r = EOK;
2681         ext4_file f;
2682         uint32_t inode;
2683         uint8_t name_index;
2684         const char *dissected_name = NULL;
2685         size_t dissected_len = 0;
2686         struct ext4_inode_ref inode_ref;
2687         struct ext4_mountpoint *mp = ext4_get_mount(path);
2688         if (!mp)
2689                 return ENOENT;
2690
2691         dissected_name = ext4_extract_xattr_name(name, name_len,
2692                                 &name_index, &dissected_len,
2693                                 &found);
2694         if (!found)
2695                 return EINVAL;
2696
2697         EXT4_MP_LOCK(mp);
2698         r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DE_UNKNOWN, NULL, NULL);
2699         if (r != EOK)
2700                 goto Finish;
2701         inode = f.inode;
2702         ext4_fclose(&f);
2703
2704         r = ext4_fs_get_inode_ref(&mp->fs, inode, &inode_ref);
2705         if (r != EOK)
2706                 goto Finish;
2707
2708         r = ext4_xattr_get(&inode_ref, name_index, dissected_name,
2709                                 dissected_len, buf, buf_size, data_size);
2710
2711         ext4_fs_put_inode_ref(&inode_ref);
2712 Finish:
2713         EXT4_MP_UNLOCK(mp);
2714         return r;
2715 }
2716
2717 int ext4_listxattr(const char *path, char *list, size_t size, size_t *ret_size)
2718 {
2719         int r = EOK;
2720         ext4_file f;
2721         uint32_t inode;
2722         size_t list_len, list_size = 0;
2723         struct ext4_inode_ref inode_ref;
2724         struct ext4_xattr_list_entry *xattr_list = NULL,
2725                                      *entry = NULL;
2726         struct ext4_mountpoint *mp = ext4_get_mount(path);
2727         if (!mp)
2728                 return ENOENT;
2729
2730         EXT4_MP_LOCK(mp);
2731         r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DE_UNKNOWN, NULL, NULL);
2732         if (r != EOK)
2733                 goto Finish;
2734         inode = f.inode;
2735         ext4_fclose(&f);
2736
2737         r = ext4_fs_get_inode_ref(&mp->fs, inode, &inode_ref);
2738         if (r != EOK)
2739                 goto Finish;
2740
2741         r = ext4_xattr_list(&inode_ref, NULL, &list_len);
2742         if (r == EOK && list_len) {
2743                 xattr_list = ext4_malloc(list_len);
2744                 if (!xattr_list) {
2745                         ext4_fs_put_inode_ref(&inode_ref);
2746                         r = ENOMEM;
2747                         goto Finish;
2748                 }
2749                 entry = xattr_list;
2750                 r = ext4_xattr_list(&inode_ref, entry, &list_len);
2751                 if (r != EOK) {
2752                         ext4_fs_put_inode_ref(&inode_ref);
2753                         goto Finish;
2754                 }
2755
2756                 for (;entry;entry = entry->next) {
2757                         size_t prefix_len;
2758                         const char *prefix =
2759                                 ext4_get_xattr_name_prefix(entry->name_index,
2760                                                            &prefix_len);
2761                         if (size) {
2762                                 if (prefix_len + entry->name_len + 1 > size) {
2763                                         ext4_fs_put_inode_ref(&inode_ref);
2764                                         r = ERANGE;
2765                                         goto Finish;
2766                                 }
2767                         }
2768
2769                         if (list && size) {
2770                                 memcpy(list, prefix, prefix_len);
2771                                 list += prefix_len;
2772                                 memcpy(list, entry->name,
2773                                         entry->name_len);
2774                                 list[entry->name_len] = 0;
2775                                 list += entry->name_len + 1;
2776
2777                                 size -= prefix_len + entry->name_len + 1;
2778                         }
2779
2780                         list_size += prefix_len + entry->name_len + 1;
2781                 }
2782                 if (ret_size)
2783                         *ret_size = list_size;
2784
2785         }
2786         ext4_fs_put_inode_ref(&inode_ref);
2787 Finish:
2788         EXT4_MP_UNLOCK(mp);
2789         if (xattr_list)
2790                 ext4_free(xattr_list);
2791
2792         return r;
2793
2794 }
2795
2796 int ext4_removexattr(const char *path, const char *name, size_t name_len)
2797 {
2798         bool found;
2799         int r = EOK;
2800         ext4_file f;
2801         uint32_t inode;
2802         uint8_t name_index;
2803         const char *dissected_name = NULL;
2804         size_t dissected_len = 0;
2805         struct ext4_inode_ref inode_ref;
2806         struct ext4_mountpoint *mp = ext4_get_mount(path);
2807         if (!mp)
2808                 return ENOENT;
2809
2810         if (mp->fs.read_only)
2811                 return EROFS;
2812
2813         dissected_name = ext4_extract_xattr_name(name, name_len,
2814                                                 &name_index, &dissected_len,
2815                                                 &found);
2816         if (!found)
2817                 return EINVAL;
2818
2819         EXT4_MP_LOCK(mp);
2820         ext4_trans_start(mp);
2821
2822         r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DE_UNKNOWN, NULL, NULL);
2823         if (r != EOK)
2824                 goto Finish;
2825         inode = f.inode;
2826         ext4_fclose(&f);
2827
2828         r = ext4_fs_get_inode_ref(&mp->fs, inode, &inode_ref);
2829         if (r != EOK)
2830                 goto Finish;
2831
2832         r = ext4_xattr_remove(&inode_ref, name_index, dissected_name,
2833                               dissected_len);
2834
2835         ext4_fs_put_inode_ref(&inode_ref);
2836 Finish:
2837         if (r != EOK)
2838                 ext4_trans_abort(mp);
2839         else
2840                 ext4_trans_stop(mp);
2841
2842         EXT4_MP_UNLOCK(mp);
2843         return r;
2844
2845 }
2846
2847 /*********************************DIRECTORY OPERATION************************/
2848
2849 int ext4_dir_rm(const char *path)
2850 {
2851         int r;
2852         int len;
2853         ext4_file f;
2854
2855         struct ext4_mountpoint *mp = ext4_get_mount(path);
2856         struct ext4_inode_ref act;
2857         struct ext4_inode_ref child;
2858         struct ext4_dir_iter it;
2859
2860         uint32_t name_off;
2861         uint32_t inode_up;
2862         uint32_t inode_current;
2863         uint32_t depth = 1;
2864
2865         bool has_children;
2866         bool is_goal;
2867         bool dir_end;
2868
2869         if (!mp)
2870                 return ENOENT;
2871
2872         if (mp->fs.read_only)
2873                 return EROFS;
2874
2875         EXT4_MP_LOCK(mp);
2876
2877         struct ext4_fs *const fs = &mp->fs;
2878
2879         /*Check if exist.*/
2880         r = ext4_generic_open(&f, path, "r", false, &inode_up, &name_off);
2881         if (r != EOK) {
2882                 ext4_trans_abort(mp);
2883                 EXT4_MP_UNLOCK(mp);
2884                 return r;
2885         }
2886
2887         path += name_off;
2888         len = ext4_path_check(path, &is_goal);
2889
2890         inode_current = f.inode;
2891
2892         ext4_block_cache_write_back(mp->fs.bdev, 1);
2893
2894         do {
2895
2896                 uint64_t act_curr_pos = 0;
2897                 has_children = false;
2898                 dir_end = false;
2899
2900                 while (r == EOK && !has_children && !dir_end) {
2901
2902                         /*Load directory node.*/
2903                         r = ext4_fs_get_inode_ref(fs, inode_current, &act);
2904                         if (r != EOK) {
2905                                 break;
2906                         }
2907
2908                         /*Initialize iterator.*/
2909                         r = ext4_dir_iterator_init(&it, &act, act_curr_pos);
2910                         if (r != EOK) {
2911                                 ext4_fs_put_inode_ref(&act);
2912                                 break;
2913                         }
2914
2915                         if (!it.curr) {
2916                                 dir_end = true;
2917                                 goto End;
2918                         }
2919
2920                         ext4_trans_start(mp);
2921
2922                         /*Get up directory inode when ".." entry*/
2923                         if ((it.curr->name_len == 2) &&
2924                             ext4_is_dots(it.curr->name, it.curr->name_len)) {
2925                                 inode_up = ext4_dir_en_get_inode(it.curr);
2926                         }
2927
2928                         /*If directory or file entry,  but not "." ".." entry*/
2929                         if (!ext4_is_dots(it.curr->name, it.curr->name_len)) {
2930
2931                                 /*Get child inode reference do unlink
2932                                  * directory/file.*/
2933                                 uint32_t cinode;
2934                                 uint32_t inode_type;
2935                                 cinode = ext4_dir_en_get_inode(it.curr);
2936                                 r = ext4_fs_get_inode_ref(fs, cinode, &child);
2937                                 if (r != EOK)
2938                                         goto End;
2939
2940                                 /*If directory with no leaf children*/
2941                                 r = ext4_has_children(&has_children, &child);
2942                                 if (r != EOK) {
2943                                         ext4_fs_put_inode_ref(&child);
2944                                         goto End;
2945                                 }
2946
2947                                 if (has_children) {
2948                                         /*Has directory children. Go into this
2949                                          * directory.*/
2950                                         inode_up = inode_current;
2951                                         inode_current = cinode;
2952                                         depth++;
2953                                         ext4_fs_put_inode_ref(&child);
2954                                         goto End;
2955                                 }
2956                                 inode_type = ext4_inode_type(&mp->fs.sb,
2957                                                 child.inode);
2958
2959                                 /* Truncate */
2960                                 if (inode_type != EXT4_INODE_MODE_DIRECTORY)
2961                                         r = ext4_trunc_inode(mp, child.index, 0);
2962                                 else
2963                                         r = ext4_trunc_dir(mp, &act, &child);
2964
2965                                 if (r != EOK) {
2966                                         ext4_fs_put_inode_ref(&child);
2967                                         goto End;
2968                                 }
2969
2970                                 /*No children in child directory or file. Just
2971                                  * unlink.*/
2972                                 r = ext4_unlink(f.mp, &act, &child,
2973                                                 (char *)it.curr->name,
2974                                                 it.curr->name_len);
2975                                 if (r != EOK) {
2976                                         ext4_fs_put_inode_ref(&child);
2977                                         goto End;
2978                                 }
2979
2980                                 ext4_inode_set_del_time(child.inode, -1L);
2981                                 ext4_inode_set_links_cnt(child.inode, 0);
2982                                 child.dirty = true;
2983
2984                                 r = ext4_fs_free_inode(&child);
2985                                 if (r != EOK) {
2986                                         ext4_fs_put_inode_ref(&child);
2987                                         goto End;
2988                                 }
2989
2990                                 r = ext4_fs_put_inode_ref(&child);
2991                                 if (r != EOK)
2992                                         goto End;
2993
2994                         }
2995
2996                         r = ext4_dir_iterator_next(&it);
2997                         if (r != EOK)
2998                                 goto End;
2999
3000                         act_curr_pos = it.curr_off;
3001 End:
3002                         ext4_dir_iterator_fini(&it);
3003                         if (r == EOK)
3004                                 r = ext4_fs_put_inode_ref(&act);
3005                         else
3006                                 ext4_fs_put_inode_ref(&act);
3007
3008                         if (r != EOK)
3009                                 ext4_trans_abort(mp);
3010                         else
3011                                 ext4_trans_stop(mp);
3012                 }
3013
3014                 if (dir_end) {
3015                         /*Directory iterator reached last entry*/
3016                         depth--;
3017                         if (depth)
3018                                 inode_current = inode_up;
3019
3020                 }
3021
3022                 if (r != EOK)
3023                         break;
3024
3025         } while (depth);
3026
3027         /*Last unlink*/
3028         if (r == EOK && !depth) {
3029                 /*Load parent.*/
3030                 struct ext4_inode_ref parent;
3031                 r = ext4_fs_get_inode_ref(&f.mp->fs, inode_up,
3032                                 &parent);
3033                 if (r != EOK)
3034                         goto Finish;
3035                 r = ext4_fs_get_inode_ref(&f.mp->fs, inode_current,
3036                                 &act);
3037                 if (r != EOK) {
3038                         ext4_fs_put_inode_ref(&act);
3039                         goto Finish;
3040                 }
3041
3042                 ext4_trans_start(mp);
3043
3044                 /* In this place all directories should be
3045                  * unlinked.
3046                  * Last unlink from root of current directory*/
3047                 r = ext4_unlink(f.mp, &parent, &act,
3048                                 (char *)path, len);
3049                 if (r != EOK) {
3050                         ext4_fs_put_inode_ref(&parent);
3051                         ext4_fs_put_inode_ref(&act);
3052                         goto Finish;
3053                 }
3054
3055                 if (ext4_inode_get_links_cnt(act.inode) == 2) {
3056                         ext4_inode_set_del_time(act.inode, -1L);
3057                         ext4_inode_set_links_cnt(act.inode, 0);
3058                         act.dirty = true;
3059                         /*Truncate*/
3060                         r = ext4_trunc_dir(mp, &parent, &act);
3061                         if (r != EOK) {
3062                                 ext4_fs_put_inode_ref(&parent);
3063                                 ext4_fs_put_inode_ref(&act);
3064                                 goto Finish;
3065                         }
3066
3067                         r = ext4_fs_free_inode(&act);
3068                         if (r != EOK) {
3069                                 ext4_fs_put_inode_ref(&parent);
3070                                 ext4_fs_put_inode_ref(&act);
3071                                 goto Finish;
3072                         }
3073                 }
3074
3075                 r = ext4_fs_put_inode_ref(&parent);
3076                 if (r != EOK)
3077                         goto Finish;
3078
3079                 r = ext4_fs_put_inode_ref(&act);
3080         Finish:
3081                 if (r != EOK)
3082                         ext4_trans_abort(mp);
3083                 else
3084                         ext4_trans_stop(mp);
3085         }
3086
3087         ext4_block_cache_write_back(mp->fs.bdev, 0);
3088         EXT4_MP_UNLOCK(mp);
3089
3090         return r;
3091 }
3092
3093 int ext4_dir_mv(const char *path, const char *new_path)
3094 {
3095         return ext4_frename(path, new_path);
3096 }
3097
3098 int ext4_dir_mk(const char *path)
3099 {
3100         int r;
3101         ext4_file f;
3102
3103         struct ext4_mountpoint *mp = ext4_get_mount(path);
3104
3105         if (!mp)
3106                 return ENOENT;
3107
3108         if (mp->fs.read_only)
3109                 return EROFS;
3110
3111         EXT4_MP_LOCK(mp);
3112         ext4_trans_start(mp);
3113
3114         /*Check if exist.*/
3115         r = ext4_generic_open(&f, path, "r", false, 0, 0);
3116         if (r == EOK) {
3117                 /*Directory already created*/
3118                 ext4_trans_stop(mp);
3119                 EXT4_MP_UNLOCK(mp);
3120                 return r;
3121         }
3122
3123         /*Create new dir*/
3124         r = ext4_generic_open(&f, path, "w", false, 0, 0);
3125         if (r != EOK) {
3126                 ext4_trans_abort(mp);
3127                 EXT4_MP_UNLOCK(mp);
3128                 return r;
3129         }
3130
3131         ext4_trans_stop(mp);
3132         EXT4_MP_UNLOCK(mp);
3133         return r;
3134 }
3135
3136 int ext4_dir_open(ext4_dir *d, const char *path)
3137 {
3138         struct ext4_mountpoint *mp = ext4_get_mount(path);
3139         int r;
3140
3141         if (!mp)
3142                 return ENOENT;
3143
3144         EXT4_MP_LOCK(mp);
3145         r = ext4_generic_open(&d->f, path, "r", false, 0, 0);
3146         d->next_off = 0;
3147         EXT4_MP_UNLOCK(mp);
3148         return r;
3149 }
3150
3151 int ext4_dir_close(ext4_dir *d)
3152 {
3153     return ext4_fclose(&d->f);
3154 }
3155
3156 const ext4_direntry *ext4_dir_entry_next(ext4_dir *d)
3157 {
3158 #define EXT4_DIR_ENTRY_OFFSET_TERM (uint64_t)(-1)
3159
3160         int r;
3161         uint16_t name_length;
3162         ext4_direntry *de = 0;
3163         struct ext4_inode_ref dir;
3164         struct ext4_dir_iter it;
3165
3166         EXT4_MP_LOCK(d->f.mp);
3167
3168         if (d->next_off == EXT4_DIR_ENTRY_OFFSET_TERM) {
3169                 EXT4_MP_UNLOCK(d->f.mp);
3170                 return 0;
3171         }
3172
3173         r = ext4_fs_get_inode_ref(&d->f.mp->fs, d->f.inode, &dir);
3174         if (r != EOK) {
3175                 goto Finish;
3176         }
3177
3178         r = ext4_dir_iterator_init(&it, &dir, d->next_off);
3179         if (r != EOK) {
3180                 ext4_fs_put_inode_ref(&dir);
3181                 goto Finish;
3182         }
3183
3184         memset(&d->de.name, 0, sizeof(d->de.name));
3185         name_length = ext4_dir_en_get_name_len(&d->f.mp->fs.sb,
3186                                                it.curr);
3187         memcpy(&d->de.name, it.curr->name, name_length);
3188
3189         /* Directly copying the content isn't safe for Big-endian targets*/
3190         d->de.inode = ext4_dir_en_get_inode(it.curr);
3191         d->de.entry_length = ext4_dir_en_get_entry_len(it.curr);
3192         d->de.name_length = name_length;
3193         d->de.inode_type = ext4_dir_en_get_inode_type(&d->f.mp->fs.sb,
3194                                                       it.curr);
3195
3196         de = &d->de;
3197
3198         ext4_dir_iterator_next(&it);
3199
3200         d->next_off = it.curr ? it.curr_off : EXT4_DIR_ENTRY_OFFSET_TERM;
3201
3202         ext4_dir_iterator_fini(&it);
3203         ext4_fs_put_inode_ref(&dir);
3204
3205 Finish:
3206         EXT4_MP_UNLOCK(d->f.mp);
3207         return de;
3208 }
3209
3210 void ext4_dir_entry_rewind(ext4_dir *d)
3211 {
3212         d->next_off = 0;
3213 }
3214
3215 /**
3216  * @}
3217  */