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