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