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