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