ext4 prefix/name string operations added.
[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 static int ext4_iterate_ea(struct ext4_xattr_ref *ref,
576                            struct ext4_xattr_item *item)
577 {
578         int i;
579         ext4_dprintf(EXT4_DEBUG_INODE, "item->name: ");
580         for (i = 0;i < item->name_len;i++)
581                 ext4_dprintf(EXT4_DEBUG_INODE, "%c", item->name[i]);
582
583         ext4_dprintf(EXT4_DEBUG_INODE, "\n");
584
585         return EXT4_XATTR_ITERATE_CONT;
586 }
587
588 /*
589  * NOTICE: if filetype is equal to EXT4_DIRENTRY_UNKNOWN,
590  * any filetype of the target dir entry will be accepted.
591  */
592 static int ext4_generic_open2(ext4_file *f, const char *path, int flags,
593                               int filetype, uint32_t *parent_inode,
594                               uint32_t *name_off)
595 {
596         bool is_goal = false;
597         uint8_t inode_type = EXT4_DIRENTRY_UNKNOWN;
598         uint32_t next_inode;
599
600         int r;
601         struct ext4_mountpoint *mp = ext4_get_mount(path);
602         struct ext4_directory_search_result result;
603         struct ext4_inode_ref ref;
604
605         f->mp = 0;
606
607         if (!mp)
608                 return ENOENT;
609
610         f->flags = flags;
611
612         /*Skip mount point*/
613         path += strlen(mp->name);
614
615         if (name_off)
616                 *name_off = strlen(mp->name);
617
618         /*Load root*/
619         r = ext4_fs_get_inode_ref(&mp->fs, EXT4_INODE_ROOT_INDEX, &ref);
620
621         if (r != EOK)
622                 return r;
623
624         if (parent_inode)
625                 *parent_inode = ref.index;
626
627         int len = ext4_path_check(path, &is_goal);
628
629         while (1) {
630
631                 len = ext4_path_check(path, &is_goal);
632
633                 if (!len) {
634                         /*If root open was request.*/
635                         if (is_goal &&
636                             ((filetype == EXT4_DIRENTRY_DIR) ||
637                              (filetype == EXT4_DIRENTRY_UNKNOWN)))
638                                 break;
639
640                         r = ENOENT;
641                         break;
642                 }
643
644                 r = ext4_dir_find_entry(&result, &ref, path, len);
645                 if (r != EOK) {
646
647                         /*Destroy last result*/
648                         ext4_dir_destroy_result(&ref, &result);
649
650                         if (r != ENOENT)
651                                 break;
652
653                         if (!(f->flags & O_CREAT))
654                                 break;
655
656                         /*O_CREAT allows create new entry*/
657                         struct ext4_inode_ref child_ref;
658                         r = ext4_fs_alloc_inode(
659                             &mp->fs, &child_ref,
660                             is_goal ? filetype
661                                     : EXT4_DIRENTRY_DIR);
662                         if (r != EOK)
663                                 break;
664
665
666                         /*Link with root dir.*/
667                         r = ext4_link(mp, &ref, &child_ref, path, len, false);
668                         if (r != EOK) {
669                                 /*Fail. Free new inode.*/
670                                 ext4_fs_free_inode(&child_ref);
671                                 /*We do not want to write new inode.
672                                   But block has to be released.*/
673                                 child_ref.dirty = false;
674                                 ext4_fs_put_inode_ref(&child_ref);
675                                 break;
676                         }
677
678                         ext4_fs_put_inode_ref(&child_ref);
679
680                         continue;
681                 }
682
683                 if (parent_inode)
684                         *parent_inode = ref.index;
685
686                 next_inode = ext4_dir_entry_ll_get_inode(result.dentry);
687                 inode_type =
688                     ext4_dir_entry_ll_get_inode_type(&mp->fs.sb, result.dentry);
689
690                 r = ext4_dir_destroy_result(&ref, &result);
691                 if (r != EOK)
692                         break;
693
694                 /*If expected file error*/
695                 if (inode_type != EXT4_DIRENTRY_DIR && !is_goal) {
696                         r = ENOENT;
697                         break;
698                 }
699                 if (filetype != EXT4_DIRENTRY_UNKNOWN) {
700                         if ((inode_type != filetype) && is_goal) {
701                                 r = ENOENT;
702                                 break;
703                         }
704                 }
705
706                 r = ext4_fs_put_inode_ref(&ref);
707                 if (r != EOK)
708                         break;
709
710                 r = ext4_fs_get_inode_ref(&mp->fs, next_inode, &ref);
711                 if (r != EOK)
712                         break;
713
714                 if (is_goal)
715                         break;
716
717                 path += len + 1;
718
719                 if (name_off)
720                         *name_off += len + 1;
721         };
722
723         if (r != EOK) {
724                 ext4_fs_put_inode_ref(&ref);
725                 return r;
726         }
727
728         if (is_goal) {
729
730                 if ((f->flags & O_TRUNC) &&
731                     (inode_type == EXT4_DIRENTRY_REG_FILE)) {
732
733                         r = ext4_fs_truncate_inode(&ref, 0);
734                         if (r != EOK) {
735                                 ext4_fs_put_inode_ref(&ref);
736                                 return r;
737                         }
738                 }
739
740                 f->mp = mp;
741                 f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);
742                 f->inode = ref.index;
743                 f->fpos = 0;
744
745                 if (f->flags & O_APPEND)
746                         f->fpos = f->fsize;
747
748         }
749
750         r = ext4_fs_put_inode_ref(&ref);
751         return r;
752 }
753
754 /****************************************************************************/
755
756 static int ext4_generic_open(ext4_file *f, const char *path, const char *flags,
757                              bool file_expect, uint32_t *parent_inode,
758                              uint32_t *name_off)
759 {
760         uint32_t iflags;
761         int filetype;
762         if (ext4_parse_flags(flags, &iflags) == false)
763                 return EINVAL;
764
765         if (file_expect == true)
766                 filetype = EXT4_DIRENTRY_REG_FILE;
767         else
768                 filetype = EXT4_DIRENTRY_DIR;
769
770         return ext4_generic_open2(f, path, iflags, filetype, parent_inode,
771                                   name_off);
772 }
773
774 static int __ext4_create_hardlink(const char *path,
775                 struct ext4_inode_ref *child_ref,
776                 bool rename)
777 {
778         bool is_goal = false;
779         uint8_t inode_type = EXT4_DIRENTRY_DIR;
780         uint32_t next_inode;
781
782         int r;
783         struct ext4_mountpoint *mp = ext4_get_mount(path);
784         struct ext4_directory_search_result result;
785         struct ext4_inode_ref ref;
786
787         if (!mp)
788                 return ENOENT;
789
790         /*Skip mount point*/
791         path += strlen(mp->name);
792
793         /*Load root*/
794         r = ext4_fs_get_inode_ref(&mp->fs, EXT4_INODE_ROOT_INDEX, &ref);
795
796         if (r != EOK)
797                 return r;
798
799         int len = ext4_path_check(path, &is_goal);
800
801         while (1) {
802
803                 len = ext4_path_check(path, &is_goal);
804
805                 if (!len) {
806                         /*If root open was request.*/
807                         if (is_goal)
808                                 r = EINVAL;
809                         else
810                                 r = ENOENT;
811                         break;
812                 }
813
814                 r = ext4_dir_find_entry(&result, &ref, path, len);
815                 if (r != EOK) {
816
817                         /*Destroy last result*/
818                         ext4_dir_destroy_result(&ref, &result);
819
820                         if (r != ENOENT || !is_goal)
821                                 break;
822
823                         /*Link with root dir.*/
824                         r = ext4_link(mp, &ref, child_ref, path, len, rename);
825                         break;
826                 } else if (r == EOK && is_goal) {
827                         /*Destroy last result*/
828                         ext4_dir_destroy_result(&ref, &result);
829                         r = EEXIST;
830                         break;
831                 }
832
833                 next_inode = result.dentry->inode;
834                 inode_type =
835                         ext4_dir_entry_ll_get_inode_type(&mp->fs.sb, result.dentry);
836
837                 r = ext4_dir_destroy_result(&ref, &result);
838                 if (r != EOK)
839                         break;
840
841                 if (inode_type == EXT4_DIRENTRY_REG_FILE) {
842                         if (is_goal)
843                                 r = EEXIST;
844                         else
845                                 r = ENOENT;
846
847                         break;
848                 }
849
850                 r = ext4_fs_put_inode_ref(&ref);
851                 if (r != EOK)
852                         break;
853
854                 r = ext4_fs_get_inode_ref(&mp->fs, next_inode, &ref);
855                 if (r != EOK)
856                         break;
857
858                 if (is_goal)
859                         break;
860
861                 path += len + 1;
862         };
863
864         if (r != EOK) {
865                 ext4_fs_put_inode_ref(&ref);
866                 return r;
867         }
868
869         r = ext4_fs_put_inode_ref(&ref);
870         return r;
871 }
872
873 static int ext4_remove_orig_reference(const char *path,
874                                       uint32_t name_off,
875                                       struct ext4_inode_ref *parent_ref,
876                                       struct ext4_inode_ref *child_ref)
877 {
878         bool is_goal;
879         int r;
880         int len;
881         struct ext4_mountpoint *mp = ext4_get_mount(path);
882
883         if (!mp)
884                 return ENOENT;
885
886         /*Set path*/
887         path += name_off;
888
889         len = ext4_path_check(path, &is_goal);
890
891         /* Remove entry from parent directory */
892         r = ext4_dir_remove_entry(parent_ref, path, len);
893         if (r != EOK)
894                 goto Finish;
895
896         if (ext4_inode_is_type(&mp->fs.sb, child_ref->inode,
897                                EXT4_INODE_MODE_DIRECTORY)) {
898                 ext4_fs_inode_links_count_dec(parent_ref);
899                 parent_ref->dirty = true;
900         }
901 Finish:
902         return r;
903 }
904
905 int ext4_flink(const char *path, const char *hardlink_path)
906 {
907         int r;
908         ext4_file f;
909         uint32_t name_off;
910         bool child_loaded = false;
911         uint32_t parent_inode, child_inode;
912         struct ext4_mountpoint *mp = ext4_get_mount(path);
913         struct ext4_mountpoint *target_mp = ext4_get_mount(hardlink_path);
914         struct ext4_inode_ref child_ref;
915
916         if (!mp)
917                 return ENOENT;
918
919         /* Will that happen? Anyway return EINVAL for such case. */
920         if (mp != target_mp)
921                 return EINVAL;
922
923         EXT4_MP_LOCK(mp);
924
925         r = ext4_generic_open2(&f, path, O_RDONLY,
926                                EXT4_DIRENTRY_UNKNOWN,
927                                &parent_inode, &name_off);
928         if (r != EOK)
929                 goto Finish;
930
931         child_inode = f.inode;
932         ext4_fclose(&f);
933
934         /*We have file to unlink. Load it.*/
935         r = ext4_fs_get_inode_ref(&mp->fs, child_inode, &child_ref);
936         if (r != EOK)
937                 goto Finish;
938
939         child_loaded = true;
940
941         /* Creating hardlink for directory is not allowed. */
942         if (ext4_inode_is_type(&mp->fs.sb, child_ref.inode,
943                                EXT4_INODE_MODE_DIRECTORY)) {
944                 r = EINVAL;
945                 goto Finish;
946         }
947
948         r = __ext4_create_hardlink(hardlink_path, &child_ref, false);
949
950 Finish:
951         if (child_loaded)
952                 ext4_fs_put_inode_ref(&child_ref);
953
954         EXT4_MP_UNLOCK(mp);
955         return r;
956
957 }
958
959 int ext4_frename(const char *path, const char *new_path)
960 {
961         int r;
962         ext4_file f;
963         uint32_t name_off;
964         bool parent_loaded = false, child_loaded = false;
965         uint32_t parent_inode, child_inode;
966         struct ext4_mountpoint *mp = ext4_get_mount(path);
967         struct ext4_inode_ref child_ref, parent_ref;
968
969         if (!mp)
970                 return ENOENT;
971
972         EXT4_MP_LOCK(mp);
973
974         r = ext4_generic_open2(&f, path, O_RDONLY,
975                         EXT4_DIRENTRY_UNKNOWN,
976                         &parent_inode, &name_off);
977         if (r != EOK)
978                 goto Finish;
979
980         child_inode = f.inode;
981         ext4_fclose(&f);
982
983         /*Load parent*/
984         r = ext4_fs_get_inode_ref(&mp->fs, parent_inode, &parent_ref);
985         if (r != EOK)
986                 goto Finish;
987
988         parent_loaded = true;
989
990         /*We have file to unlink. Load it.*/
991         r = ext4_fs_get_inode_ref(&mp->fs, child_inode, &child_ref);
992         if (r != EOK)
993                 goto Finish;
994
995         child_loaded = true;
996
997         r = __ext4_create_hardlink(new_path, &child_ref, true);
998         if (r != EOK)
999                 goto Finish;
1000
1001         r = ext4_remove_orig_reference(path, name_off,
1002                                        &parent_ref, &child_ref);
1003         if (r != EOK)
1004                 goto Finish;
1005
1006 Finish:
1007         if (parent_loaded)
1008                 ext4_fs_put_inode_ref(&parent_ref);
1009
1010         if (child_loaded)
1011                 ext4_fs_put_inode_ref(&child_ref);
1012
1013         EXT4_MP_UNLOCK(mp);
1014         return r;
1015
1016 }
1017
1018 /****************************************************************************/
1019
1020 int ext4_get_sblock(const char *mount_point, struct ext4_sblock **sb)
1021 {
1022         struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
1023
1024         if (!mp)
1025                 return ENOENT;
1026
1027         *sb = &mp->fs.sb;
1028         return EOK;
1029 }
1030
1031 int ext4_cache_write_back(const char *path, bool on)
1032 {
1033         struct ext4_mountpoint *mp = ext4_get_mount(path);
1034
1035         if (!mp)
1036                 return ENOENT;
1037
1038         EXT4_MP_LOCK(mp);
1039         ext4_block_cache_write_back(mp->fs.bdev, on);
1040         EXT4_MP_UNLOCK(mp);
1041         return EOK;
1042 }
1043
1044 int ext4_fremove(const char *path)
1045 {
1046         ext4_file f;
1047         uint32_t parent_inode;
1048         uint32_t name_off;
1049         bool is_goal;
1050         int r;
1051         int len;
1052         struct ext4_inode_ref child;
1053         struct ext4_inode_ref parent;
1054         struct ext4_mountpoint *mp = ext4_get_mount(path);
1055
1056         if (!mp)
1057                 return ENOENT;
1058
1059         EXT4_MP_LOCK(mp);
1060         r = ext4_generic_open2(&f, path, O_RDWR,
1061                                EXT4_DIRENTRY_UNKNOWN,
1062                                &parent_inode, &name_off);
1063         if (r != EOK) {
1064                 EXT4_MP_UNLOCK(mp);
1065                 return r;
1066         }
1067
1068         /*Load parent*/
1069         r = ext4_fs_get_inode_ref(&mp->fs, parent_inode, &parent);
1070         if (r != EOK) {
1071                 EXT4_MP_UNLOCK(mp);
1072                 return r;
1073         }
1074
1075         /*We have file to delete. Load it.*/
1076         r = ext4_fs_get_inode_ref(&mp->fs, f.inode, &child);
1077         if (r != EOK) {
1078                 ext4_fs_put_inode_ref(&parent);
1079                 EXT4_MP_UNLOCK(mp);
1080                 return r;
1081         }
1082
1083         /*Set path*/
1084         path += name_off;
1085
1086         len = ext4_path_check(path, &is_goal);
1087
1088         /*Unlink from parent*/
1089         r = ext4_unlink(mp, &parent, &child, path, len);
1090         if (r != EOK)
1091                 goto Finish;
1092
1093         /*Link count is zero, the inode should be freed. */
1094         if (!ext4_inode_get_links_count(child.inode)) {
1095                 ext4_inode_set_deletion_time(child.inode, 0xFFFFFFFF);
1096                 /*Turncate*/
1097                 ext4_block_cache_write_back(mp->fs.bdev, 1);
1098                 /*Truncate may be IO heavy. Do it writeback cache mode.*/
1099                 r = ext4_fs_truncate_inode(&child, 0);
1100                 ext4_block_cache_write_back(mp->fs.bdev, 0);
1101
1102                 if (r != EOK)
1103                         goto Finish;
1104
1105                 r = ext4_fs_free_inode(&child);
1106                 if (r != EOK)
1107                         goto Finish;
1108         }
1109
1110 Finish:
1111         ext4_fs_put_inode_ref(&child);
1112         ext4_fs_put_inode_ref(&parent);
1113         EXT4_MP_UNLOCK(mp);
1114         return r;
1115 }
1116
1117 int ext4_fill_raw_inode(const char *path,
1118                         uint32_t *ret_ino,
1119                         struct ext4_inode *inode)
1120 {
1121         int r;
1122         ext4_file f;
1123         struct ext4_inode_ref inode_ref;
1124         struct ext4_mountpoint *mp = ext4_get_mount(path);
1125         uint32_t ino;
1126
1127         if (!mp)
1128                 return ENOENT;
1129
1130         EXT4_MP_LOCK(mp);
1131
1132         r = ext4_generic_open2(&f, path, O_RDONLY,
1133                                EXT4_DIRENTRY_UNKNOWN,
1134                                NULL, NULL);
1135         if (r != EOK) {
1136                 EXT4_MP_UNLOCK(mp);
1137                 return r;
1138         }
1139
1140         ino = f.inode;
1141         ext4_fclose(&f);
1142
1143         /*Load parent*/
1144         r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
1145         if (r != EOK) {
1146                 EXT4_MP_UNLOCK(mp);
1147                 return r;
1148         }
1149
1150         memcpy(inode, inode_ref.inode, sizeof(struct ext4_inode));
1151
1152         ext4_fs_put_inode_ref(&inode_ref);
1153         EXT4_MP_UNLOCK(mp);
1154
1155         if (ret_ino)
1156                 *ret_ino = ino;
1157
1158         return r;
1159 }
1160
1161 int ext4_fopen(ext4_file *f, const char *path, const char *flags)
1162 {
1163         struct ext4_mountpoint *mp = ext4_get_mount(path);
1164         int r;
1165
1166         if (!mp)
1167                 return ENOENT;
1168
1169         EXT4_MP_LOCK(mp);
1170         ext4_block_cache_write_back(mp->fs.bdev, 1);
1171         r = ext4_generic_open(f, path, flags, true, 0, 0);
1172         ext4_block_cache_write_back(mp->fs.bdev, 0);
1173         EXT4_MP_UNLOCK(mp);
1174         return r;
1175 }
1176
1177 int ext4_fopen2(ext4_file *f, const char *path, int flags)
1178 {
1179         struct ext4_mountpoint *mp = ext4_get_mount(path);
1180         int r;
1181         int filetype;
1182
1183         if (!mp)
1184                 return ENOENT;
1185
1186         filetype = EXT4_DIRENTRY_REG_FILE;
1187
1188         EXT4_MP_LOCK(mp);
1189         ext4_block_cache_write_back(mp->fs.bdev, 1);
1190         r = ext4_generic_open2(f, path, flags, filetype, 0, 0);
1191         ext4_block_cache_write_back(mp->fs.bdev, 0);
1192         EXT4_MP_UNLOCK(mp);
1193         return r;
1194 }
1195
1196 int ext4_fclose(ext4_file *f)
1197 {
1198         ext4_assert(f && f->mp);
1199
1200         f->mp = 0;
1201         f->flags = 0;
1202         f->inode = 0;
1203         f->fpos = f->fsize = 0;
1204
1205         return EOK;
1206 }
1207
1208 static int ext4_ftruncate_no_lock(ext4_file *f, uint64_t size)
1209 {
1210         struct ext4_inode_ref ref;
1211         int r;
1212
1213
1214         r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
1215         if (r != EOK) {
1216                 EXT4_MP_UNLOCK(f->mp);
1217                 return r;
1218         }
1219
1220         /*Sync file size*/
1221         f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);
1222         if (f->fsize <= size) {
1223                 r = EOK;
1224                 goto Finish;
1225         }
1226
1227         /*Start write back cache mode.*/
1228         r = ext4_block_cache_write_back(f->mp->fs.bdev, 1);
1229         if (r != EOK)
1230                 goto Finish;
1231
1232         r = ext4_fs_truncate_inode(&ref, size);
1233         if (r != EOK)
1234                 goto Finish;
1235
1236         f->fsize = size;
1237         if (f->fpos > size)
1238                 f->fpos = size;
1239
1240         /*Stop write back cache mode*/
1241         ext4_block_cache_write_back(f->mp->fs.bdev, 0);
1242
1243         if (r != EOK)
1244                 goto Finish;
1245
1246 Finish:
1247         ext4_fs_put_inode_ref(&ref);
1248         return r;
1249
1250 }
1251
1252 int ext4_ftruncate(ext4_file *f, uint64_t size)
1253 {
1254         int r;
1255         ext4_assert(f && f->mp);
1256
1257         if (f->flags & O_RDONLY)
1258                 return EPERM;
1259
1260         EXT4_MP_LOCK(f->mp);
1261
1262         r = ext4_ftruncate_no_lock(f, size);
1263
1264         EXT4_MP_UNLOCK(f->mp);
1265         return r;
1266 }
1267
1268 int ext4_fread(ext4_file *f, void *buf, size_t size, size_t *rcnt)
1269 {
1270         uint32_t u;
1271         uint32_t fblock;
1272         uint32_t fblock_start;
1273         uint32_t fblock_cnt;
1274         uint32_t sblock;
1275         uint32_t sblock_end;
1276         uint32_t block_size;
1277         uint8_t *u8_buf = buf;
1278         int r;
1279         struct ext4_block b;
1280         struct ext4_inode_ref ref;
1281
1282         ext4_assert(f && f->mp);
1283
1284         if (f->flags & O_WRONLY)
1285                 return EPERM;
1286
1287         if (!size)
1288                 return EOK;
1289
1290         EXT4_MP_LOCK(f->mp);
1291
1292         if (rcnt)
1293                 *rcnt = 0;
1294
1295         r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
1296         if (r != EOK) {
1297                 EXT4_MP_UNLOCK(f->mp);
1298                 return r;
1299         }
1300
1301         /*Sync file size*/
1302         f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);
1303
1304         block_size = ext4_sb_get_block_size(&f->mp->fs.sb);
1305         size = size > (f->fsize - f->fpos) ? (f->fsize - f->fpos) : size;
1306         sblock = (f->fpos) / block_size;
1307         sblock_end = (f->fpos + size) / block_size;
1308         u = (f->fpos) % block_size;
1309
1310         /*If the size of symlink is smaller than 60 bytes*/
1311         if (ext4_inode_is_type(&f->mp->fs.sb, ref.inode, EXT4_INODE_MODE_SOFTLINK)
1312                         && f->fsize < sizeof(ref.inode->blocks)
1313                         && !ext4_inode_get_blocks_count(&f->mp->fs.sb, ref.inode)) {
1314                 char *content = (char *)ref.inode->blocks;
1315                 if (f->fpos < f->fsize) {
1316                         r = (u + size > f->fsize)
1317                                 ?(f->fsize - u)
1318                                 :(size);
1319                         memcpy(buf, content + u, r);
1320                         if (rcnt)
1321                                 *rcnt = r;
1322
1323                 } else {
1324                         r = 0;
1325                         if (rcnt)
1326                                 *rcnt = 0;
1327
1328                 }
1329
1330                 r = EOK;
1331                 goto Finish;
1332         }
1333
1334         if (u) {
1335
1336                 uint32_t ll = size > (block_size - u) ? (block_size - u) : size;
1337
1338                 r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);
1339                 if (r != EOK)
1340                         goto Finish;
1341
1342                 r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
1343                 if (r != EOK)
1344                         goto Finish;
1345
1346                 memcpy(u8_buf, b.data + u, ll);
1347
1348                 r = ext4_block_set(f->mp->fs.bdev, &b);
1349                 if (r != EOK)
1350                         goto Finish;
1351
1352                 u8_buf += ll;
1353                 size -= ll;
1354                 f->fpos += ll;
1355
1356                 if (rcnt)
1357                         *rcnt += ll;
1358
1359                 sblock++;
1360         }
1361
1362         fblock_start = 0;
1363         fblock_cnt = 0;
1364         while (size >= block_size) {
1365                 while (sblock < sblock_end) {
1366                         r = ext4_fs_get_inode_data_block_index(&ref, sblock,
1367                                                                &fblock);
1368                         if (r != EOK)
1369                                 goto Finish;
1370
1371                         sblock++;
1372
1373                         if (!fblock_start) {
1374                                 fblock_start = fblock;
1375                         }
1376
1377                         if ((fblock_start + fblock_cnt) != fblock)
1378                                 break;
1379
1380                         fblock_cnt++;
1381                 }
1382
1383                 r = ext4_blocks_get_direct(f->mp->fs.bdev, u8_buf, fblock_start,
1384                                            fblock_cnt);
1385                 if (r != EOK)
1386                         goto Finish;
1387
1388                 size -= block_size * fblock_cnt;
1389                 u8_buf += block_size * fblock_cnt;
1390                 f->fpos += block_size * fblock_cnt;
1391
1392                 if (rcnt)
1393                         *rcnt += block_size * fblock_cnt;
1394
1395                 fblock_start = fblock;
1396                 fblock_cnt = 1;
1397         }
1398
1399         if (size) {
1400                 r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);
1401                 if (r != EOK)
1402                         goto Finish;
1403
1404                 r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
1405                 if (r != EOK)
1406                         goto Finish;
1407
1408                 memcpy(u8_buf, b.data, size);
1409
1410                 r = ext4_block_set(f->mp->fs.bdev, &b);
1411                 if (r != EOK)
1412                         goto Finish;
1413
1414                 f->fpos += size;
1415
1416                 if (rcnt)
1417                         *rcnt += size;
1418         }
1419
1420 Finish:
1421         ext4_fs_put_inode_ref(&ref);
1422         EXT4_MP_UNLOCK(f->mp);
1423         return r;
1424 }
1425
1426 int ext4_fwrite(ext4_file *f, const void *buf, size_t size, size_t *wcnt)
1427 {
1428         uint32_t u;
1429         uint32_t fblock;
1430
1431         uint32_t sblock;
1432         uint32_t sblock_end;
1433         uint32_t file_blocks;
1434         uint32_t block_size;
1435         uint32_t fblock_start;
1436         uint32_t fblock_cnt;
1437
1438         struct ext4_block b;
1439         struct ext4_inode_ref ref;
1440         const uint8_t *u8_buf = buf;
1441         int r;
1442
1443         ext4_assert(f && f->mp);
1444
1445         if (f->flags & O_RDONLY)
1446                 return EPERM;
1447
1448         if (!size)
1449                 return EOK;
1450
1451         EXT4_MP_LOCK(f->mp);
1452
1453         if (wcnt)
1454                 *wcnt = 0;
1455
1456         r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
1457         if (r != EOK) {
1458                 EXT4_MP_UNLOCK(f->mp);
1459                 return r;
1460         }
1461
1462         /*Sync file size*/
1463         f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);
1464
1465         block_size = ext4_sb_get_block_size(&f->mp->fs.sb);
1466
1467         sblock_end = (f->fpos + size) > f->fsize ? (f->fpos + size) : f->fsize;
1468         sblock_end /= block_size;
1469         file_blocks = (f->fsize / block_size);
1470
1471         if (f->fsize % block_size)
1472                 file_blocks++;
1473
1474         sblock = (f->fpos) / block_size;
1475
1476         u = (f->fpos) % block_size;
1477
1478         if (u) {
1479                 uint32_t ll = size > (block_size - u) ? (block_size - u) : size;
1480
1481                 r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);
1482                 if (r != EOK)
1483                         goto Finish;
1484
1485                 r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
1486                 if (r != EOK)
1487                         goto Finish;
1488
1489                 memcpy(b.data + u, u8_buf, ll);
1490                 b.dirty = true;
1491
1492                 r = ext4_block_set(f->mp->fs.bdev, &b);
1493                 if (r != EOK)
1494                         goto Finish;
1495
1496                 u8_buf += ll;
1497                 size -= ll;
1498                 f->fpos += ll;
1499
1500                 if (wcnt)
1501                         *wcnt += ll;
1502
1503                 sblock++;
1504         }
1505
1506         /*Start write back cache mode.*/
1507         r = ext4_block_cache_write_back(f->mp->fs.bdev, 1);
1508         if (r != EOK)
1509                 goto Finish;
1510
1511         fblock_start = 0;
1512         fblock_cnt = 0;
1513         while (size >= block_size) {
1514
1515                 while (sblock < sblock_end) {
1516                         if (sblock < file_blocks) {
1517                                 r = ext4_fs_get_inode_data_block_index(
1518                                     &ref, sblock, &fblock);
1519                                 if (r != EOK)
1520                                         break;
1521                         } else {
1522                                 r = ext4_fs_append_inode_block(&ref, &fblock,
1523                                                                &sblock);
1524                                 if (r != EOK)
1525                                         break;
1526                         }
1527
1528                         sblock++;
1529
1530                         if (!fblock_start) {
1531                                 fblock_start = fblock;
1532                         }
1533
1534                         if ((fblock_start + fblock_cnt) != fblock)
1535                                 break;
1536
1537                         fblock_cnt++;
1538                 }
1539
1540                 r = ext4_blocks_set_direct(f->mp->fs.bdev, u8_buf, fblock_start,
1541                                            fblock_cnt);
1542                 if (r != EOK)
1543                         break;
1544
1545                 size -= block_size * fblock_cnt;
1546                 u8_buf += block_size * fblock_cnt;
1547                 f->fpos += block_size * fblock_cnt;
1548
1549                 if (wcnt)
1550                         *wcnt += block_size * fblock_cnt;
1551
1552                 fblock_start = fblock;
1553                 fblock_cnt = 1;
1554         }
1555
1556         /*Stop write back cache mode*/
1557         ext4_block_cache_write_back(f->mp->fs.bdev, 0);
1558
1559         if (r != EOK)
1560                 goto Finish;
1561
1562         if (size) {
1563                 if (sblock < file_blocks) {
1564                         r = ext4_fs_get_inode_data_block_index(&ref, sblock,
1565                                                                &fblock);
1566                         if (r != EOK)
1567                                 goto Finish;
1568                 } else {
1569                         r = ext4_fs_append_inode_block(&ref, &fblock, &sblock);
1570                         if (r != EOK)
1571                                 goto Finish;
1572                 }
1573
1574                 r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
1575                 if (r != EOK)
1576                         goto Finish;
1577
1578                 memcpy(b.data, u8_buf, size);
1579                 b.dirty = true;
1580
1581                 r = ext4_block_set(f->mp->fs.bdev, &b);
1582                 if (r != EOK)
1583                         goto Finish;
1584
1585                 f->fpos += size;
1586
1587                 if (wcnt)
1588                         *wcnt += size;
1589         }
1590
1591         if (f->fpos > f->fsize) {
1592                 f->fsize = f->fpos;
1593                 ext4_inode_set_size(ref.inode, f->fsize);
1594                 ref.dirty = true;
1595         }
1596
1597 Finish:
1598         ext4_fs_put_inode_ref(&ref);
1599         EXT4_MP_UNLOCK(f->mp);
1600         return r;
1601 }
1602
1603 int ext4_fseek(ext4_file *f, uint64_t offset, uint32_t origin)
1604 {
1605         switch (origin) {
1606         case SEEK_SET:
1607                 if (offset > f->fsize)
1608                         return EINVAL;
1609
1610                 f->fpos = offset;
1611                 return EOK;
1612         case SEEK_CUR:
1613                 if ((offset + f->fpos) > f->fsize)
1614                         return EINVAL;
1615
1616                 f->fpos += offset;
1617                 return EOK;
1618         case SEEK_END:
1619                 if (offset > f->fsize)
1620                         return EINVAL;
1621
1622                 f->fpos = f->fsize - offset;
1623                 return EOK;
1624         }
1625         return EINVAL;
1626 }
1627
1628 uint64_t ext4_ftell(ext4_file *f) { return f->fpos; }
1629
1630 uint64_t ext4_fsize(ext4_file *f) { return f->fsize; }
1631
1632 int ext4_chmod(const char *path, uint32_t mode)
1633 {
1634         int r;
1635         uint32_t ino;
1636         ext4_file f;
1637         struct ext4_sblock *sb;
1638         struct ext4_inode_ref inode_ref;
1639         struct ext4_mountpoint *mp = ext4_get_mount(path);
1640
1641         if (!mp)
1642                 return ENOENT;
1643
1644         EXT4_MP_LOCK(mp);
1645
1646         r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DIRENTRY_UNKNOWN, 0, 0);
1647         if (r != EOK) {
1648                 EXT4_MP_UNLOCK(mp);
1649                 return r;
1650         }
1651         ino = f.inode;
1652         sb = &mp->fs.sb;
1653         ext4_fclose(&f);
1654         r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
1655         if (r != EOK) {
1656                 EXT4_MP_UNLOCK(mp);
1657                 return r;
1658         }
1659
1660         ext4_inode_set_mode(sb, inode_ref.inode, mode);
1661         inode_ref.dirty = true;
1662
1663         ext4_fs_put_inode_ref(&inode_ref);
1664         EXT4_MP_UNLOCK(mp);
1665         return r;
1666 }
1667
1668 int ext4_chown(const char *path, uint32_t uid, uint32_t gid)
1669 {
1670         int r;
1671         ext4_file f;
1672         uint32_t ino;
1673         struct ext4_inode_ref inode_ref;
1674         struct ext4_mountpoint *mp = ext4_get_mount(path);
1675
1676         if (!mp)
1677                 return ENOENT;
1678
1679         EXT4_MP_LOCK(mp);
1680
1681         r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DIRENTRY_UNKNOWN, 0, 0);
1682         if (r != EOK) {
1683                 EXT4_MP_UNLOCK(mp);
1684                 return r;
1685         }
1686         ino = f.inode;
1687         ext4_fclose(&f);
1688         r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
1689         if (r != EOK) {
1690                 EXT4_MP_UNLOCK(mp);
1691                 return r;
1692         }
1693
1694         ext4_inode_set_uid(inode_ref.inode, uid);
1695         ext4_inode_set_gid(inode_ref.inode, gid);
1696         inode_ref.dirty = true;
1697
1698         ext4_fs_put_inode_ref(&inode_ref);
1699         EXT4_MP_UNLOCK(mp);
1700         return r;
1701 }
1702
1703 int ext4_file_set_atime(const char *path, uint32_t atime)
1704 {
1705         int r;
1706         ext4_file f;
1707         uint32_t ino;
1708         struct ext4_inode_ref inode_ref;
1709         struct ext4_mountpoint *mp = ext4_get_mount(path);
1710
1711         if (!mp)
1712                 return ENOENT;
1713
1714         EXT4_MP_LOCK(mp);
1715
1716         r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DIRENTRY_UNKNOWN, 0, 0);
1717         if (r != EOK) {
1718                 EXT4_MP_UNLOCK(mp);
1719                 return r;
1720         }
1721         ino = f.inode;
1722         ext4_fclose(&f);
1723         r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
1724         if (r != EOK) {
1725                 EXT4_MP_UNLOCK(mp);
1726                 return r;
1727         }
1728
1729         ext4_inode_set_access_time(inode_ref.inode, atime);
1730         inode_ref.dirty = true;
1731
1732         ext4_fs_put_inode_ref(&inode_ref);
1733         EXT4_MP_UNLOCK(mp);
1734         return r;
1735 }
1736
1737 int ext4_file_set_mtime(const char *path, uint32_t mtime)
1738 {
1739         int r;
1740         ext4_file f;
1741         uint32_t ino;
1742         struct ext4_inode_ref inode_ref;
1743         struct ext4_mountpoint *mp = ext4_get_mount(path);
1744
1745         if (!mp)
1746                 return ENOENT;
1747
1748         EXT4_MP_LOCK(mp);
1749
1750         r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DIRENTRY_UNKNOWN, 0, 0);
1751         if (r != EOK) {
1752                 EXT4_MP_UNLOCK(mp);
1753                 return r;
1754         }
1755         ino = f.inode;
1756         ext4_fclose(&f);
1757         r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
1758         if (r != EOK) {
1759                 EXT4_MP_UNLOCK(mp);
1760                 return r;
1761         }
1762
1763         ext4_inode_set_modification_time(inode_ref.inode, mtime);
1764         inode_ref.dirty = true;
1765
1766         ext4_fs_put_inode_ref(&inode_ref);
1767         EXT4_MP_UNLOCK(mp);
1768         return r;
1769 }
1770
1771 int ext4_file_set_ctime(const char *path, uint32_t ctime)
1772 {
1773         int r;
1774         ext4_file f;
1775         uint32_t ino;
1776         struct ext4_inode_ref inode_ref;
1777         struct ext4_mountpoint *mp = ext4_get_mount(path);
1778
1779         if (!mp)
1780                 return ENOENT;
1781
1782         EXT4_MP_LOCK(mp);
1783
1784         r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DIRENTRY_UNKNOWN, 0, 0);
1785         if (r != EOK) {
1786                 EXT4_MP_UNLOCK(mp);
1787                 return r;
1788         }
1789         ino = f.inode;
1790         ext4_fclose(&f);
1791         r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
1792         if (r != EOK) {
1793                 EXT4_MP_UNLOCK(mp);
1794                 return r;
1795         }
1796
1797         ext4_inode_set_change_inode_time(inode_ref.inode, ctime);
1798         inode_ref.dirty = true;
1799
1800         ext4_fs_put_inode_ref(&inode_ref);
1801         EXT4_MP_UNLOCK(mp);
1802         return r;
1803 }
1804
1805 static int ext4_fsymlink_set(ext4_file *f, const void *buf, uint32_t size)
1806 {
1807         struct ext4_block b;
1808         struct ext4_inode_ref ref;
1809         uint32_t sblock, fblock;
1810         uint32_t block_size;
1811         int r;
1812
1813         ext4_assert(f && f->mp);
1814
1815         if (!size)
1816                 return EOK;
1817
1818         r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
1819         if (r != EOK) {
1820                 EXT4_MP_UNLOCK(f->mp);
1821                 return r;
1822         }
1823
1824         /*Sync file size*/
1825         block_size = ext4_sb_get_block_size(&f->mp->fs.sb);
1826         if (size > block_size) {
1827                 r = EINVAL;
1828                 goto Finish;
1829         }
1830         r = ext4_ftruncate_no_lock(f, 0);
1831         if (r != EOK)
1832                 goto Finish;
1833
1834         /*Start write back cache mode.*/
1835         r = ext4_block_cache_write_back(f->mp->fs.bdev, 1);
1836         if (r != EOK)
1837                 goto Finish;
1838
1839         /*If the size of symlink is smaller than 60 bytes*/
1840         if (size < sizeof(ref.inode->blocks)) {
1841                 char *content = (char *)ref.inode->blocks;
1842                 memset(content, 0, sizeof(ref.inode->blocks));
1843                 memcpy(content, buf, size);
1844                 ext4_inode_clear_flag(ref.inode, EXT4_INODE_FLAG_EXTENTS);
1845         } else {
1846                 ext4_fs_inode_blocks_init(&f->mp->fs, &ref);
1847                 r = ext4_fs_append_inode_block(&ref, &fblock, &sblock);
1848                 if (r != EOK)
1849                         goto Finish;
1850
1851                 r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
1852                 if (r != EOK)
1853                         goto Finish;
1854
1855                 memcpy(b.data, buf, size);
1856                 b.dirty = true;
1857                 r = ext4_block_set(f->mp->fs.bdev, &b);
1858                 if (r != EOK)
1859                         goto Finish;
1860         }
1861
1862         /*Stop write back cache mode*/
1863         ext4_block_cache_write_back(f->mp->fs.bdev, 0);
1864
1865         if (r != EOK)
1866                 goto Finish;
1867
1868         ext4_inode_set_size(ref.inode, size);
1869         ref.dirty = true;
1870
1871         f->fsize = size;
1872         if (f->fpos > size)
1873                 f->fpos = size;
1874
1875 Finish:
1876         ext4_fs_put_inode_ref(&ref);
1877         return r;
1878 }
1879
1880 int ext4_fsymlink(const char *target, const char *path)
1881 {
1882         struct ext4_mountpoint *mp = ext4_get_mount(path);
1883         int r;
1884         ext4_file f;
1885         int filetype;
1886
1887         if (!mp)
1888                 return ENOENT;
1889
1890         filetype = EXT4_DIRENTRY_SYMLINK;
1891
1892         EXT4_MP_LOCK(mp);
1893         ext4_block_cache_write_back(mp->fs.bdev, 1);
1894         r = ext4_generic_open2(&f, path, O_RDWR|O_CREAT, filetype, 0, 0);
1895         if (r == EOK)
1896                 r = ext4_fsymlink_set(&f, target, strlen(target));
1897         else
1898                 goto Finish;
1899
1900         ext4_fclose(&f);
1901
1902 Finish:
1903         ext4_block_cache_write_back(mp->fs.bdev, 0);
1904         EXT4_MP_UNLOCK(mp);
1905         return r;
1906 }
1907
1908 int ext4_readlink(const char *path, char *buf, size_t bufsize, size_t *rcnt)
1909 {
1910         struct ext4_mountpoint *mp = ext4_get_mount(path);
1911         int r;
1912         ext4_file f;
1913         int filetype;
1914
1915         if (!mp)
1916                 return ENOENT;
1917
1918         if (!buf)
1919                 return EINVAL;
1920
1921         memset(buf, 0, sizeof(bufsize));
1922
1923         filetype = EXT4_DIRENTRY_SYMLINK;
1924
1925         EXT4_MP_LOCK(mp);
1926         ext4_block_cache_write_back(mp->fs.bdev, 1);
1927         r = ext4_generic_open2(&f, path, O_RDONLY, filetype, 0, 0);
1928         if (r == EOK)
1929                 r = ext4_fread(&f, buf, bufsize, rcnt);
1930         else
1931                 goto Finish;
1932
1933         ext4_fclose(&f);
1934
1935 Finish:
1936         ext4_block_cache_write_back(mp->fs.bdev, 0);
1937         EXT4_MP_UNLOCK(mp);
1938         return r;
1939 }
1940
1941 int ext4_fsetxattr(ext4_file *file,
1942                    char *name,
1943                    size_t name_len,
1944                    void *data,
1945                    size_t data_size,
1946                    bool replace)
1947 {
1948
1949 }
1950
1951 /*********************************DIRECTORY OPERATION************************/
1952
1953 int ext4_dir_rm(const char *path)
1954 {
1955         int r;
1956         int len;
1957         ext4_file f;
1958
1959         struct ext4_mountpoint *mp = ext4_get_mount(path);
1960         struct ext4_inode_ref current;
1961         struct ext4_inode_ref child;
1962         struct ext4_directory_iterator it;
1963
1964         uint32_t name_off;
1965         uint32_t inode_up;
1966         uint32_t inode_current;
1967         uint32_t depth = 1;
1968
1969         bool has_children;
1970         bool is_goal;
1971         bool dir_end;
1972
1973         if (!mp)
1974                 return ENOENT;
1975
1976         EXT4_MP_LOCK(mp);
1977
1978         /*Check if exist.*/
1979         r = ext4_generic_open(&f, path, "r", false, &inode_up, &name_off);
1980         if (r != EOK) {
1981                 EXT4_MP_UNLOCK(mp);
1982                 return r;
1983         }
1984
1985         path += name_off;
1986         len = ext4_path_check(path, &is_goal);
1987
1988         inode_current = f.inode;
1989         dir_end = false;
1990
1991         ext4_block_cache_write_back(mp->fs.bdev, 1);
1992
1993         do {
1994                 /*Load directory node.*/
1995                 r = ext4_fs_get_inode_ref(&f.mp->fs, inode_current, &current);
1996                 if (r != EOK) {
1997                         break;
1998                 }
1999
2000                 /*Initialize iterator.*/
2001                 r = ext4_dir_iterator_init(&it, &current, 0);
2002                 if (r != EOK) {
2003                         ext4_fs_put_inode_ref(&current);
2004                         break;
2005                 }
2006
2007                 while (r == EOK) {
2008
2009                         if (!it.current) {
2010                                 dir_end = true;
2011                                 break;
2012                         }
2013
2014                         /*Get up directory inode when ".." entry*/
2015                         if ((it.current->name_length == 2) &&
2016                             ext4_is_dots(it.current->name,
2017                                          it.current->name_length)) {
2018                                 inode_up = ext4_dir_entry_ll_get_inode(it.current);
2019                         }
2020
2021                         /*If directory or file entry,  but not "." ".." entry*/
2022                         if (!ext4_is_dots(it.current->name,
2023                                           it.current->name_length)) {
2024
2025                                 /*Get child inode reference do unlink
2026                                  * directory/file.*/
2027                                 r = ext4_fs_get_inode_ref(&f.mp->fs,
2028                                         ext4_dir_entry_ll_get_inode(it.current),
2029                                         &child);
2030                                 if (r != EOK)
2031                                         break;
2032
2033                                 /*If directory with no leaf children*/
2034                                 r = ext4_has_children(&has_children, &child);
2035                                 if (r != EOK) {
2036                                         ext4_fs_put_inode_ref(&child);
2037                                         break;
2038                                 }
2039
2040                                 if (has_children) {
2041                                         /*Has directory children. Go into this
2042                                          * directory.*/
2043                                         inode_up = inode_current;
2044                                         inode_current = ext4_dir_entry_ll_get_inode(it.current);
2045                                         depth++;
2046                                         ext4_fs_put_inode_ref(&child);
2047                                         break;
2048                                 }
2049
2050                                 /*No children in child directory or file. Just
2051                                  * unlink.*/
2052                                 r = ext4_unlink(f.mp, &current, &child,
2053                                                 (char *)it.current->name,
2054                                                 it.current->name_length);
2055                                 if (r != EOK) {
2056                                         ext4_fs_put_inode_ref(&child);
2057                                         break;
2058                                 }
2059
2060                                 ext4_inode_set_deletion_time(child.inode,
2061                                                              0xFFFFFFFF);
2062                                 ext4_inode_set_links_count(child.inode, 0);
2063                                 child.dirty = true;
2064                                 /*Turncate*/
2065                                 r = ext4_fs_truncate_inode(&child, 0);
2066                                 if (r != EOK) {
2067                                         ext4_fs_put_inode_ref(&child);
2068                                         break;
2069                                 }
2070
2071                                 r = ext4_fs_free_inode(&child);
2072                                 if (r != EOK) {
2073                                         ext4_fs_put_inode_ref(&child);
2074                                         break;
2075                                 }
2076
2077                                 r = ext4_fs_put_inode_ref(&child);
2078                                 if (r != EOK)
2079                                         break;
2080                         }
2081
2082                         r = ext4_dir_iterator_next(&it);
2083                 }
2084
2085                 if (dir_end) {
2086                         /*Directory iterator reached last entry*/
2087                         ext4_has_children(&has_children, &current);
2088                         if (!has_children) {
2089                                 inode_current = inode_up;
2090                                 if (depth)
2091                                         depth--;
2092                         }
2093                         /*Last unlink*/
2094                         if (!depth) {
2095                                 /*Load parent.*/
2096                                 struct ext4_inode_ref parent;
2097                                 r = ext4_fs_get_inode_ref(&f.mp->fs, inode_up,
2098                                                           &parent);
2099                                 if (r != EOK)
2100                                         goto End;
2101
2102                                 /* In this place all directories should be
2103                                  * unlinked.
2104                                  * Last unlink from root of current directory*/
2105                                 r = ext4_unlink(f.mp, &parent, &current,
2106                                                 (char *)path, len);
2107                                 if (r != EOK) {
2108                                         ext4_fs_put_inode_ref(&parent);
2109                                         goto End;
2110                                 }
2111
2112                                 if (ext4_inode_get_links_count(current.inode) ==
2113                                     2) {
2114                                         ext4_inode_set_deletion_time(
2115                                             current.inode, 0xFFFFFFFF);
2116                                         ext4_inode_set_links_count(
2117                                             current.inode, 0);
2118                                         current.dirty = true;
2119                                         /*Turncate*/
2120                                         r = ext4_fs_truncate_inode(&current, 0);
2121                                         if (r != EOK) {
2122                                                 ext4_fs_put_inode_ref(&parent);
2123                                                 goto End;
2124                                         }
2125
2126                                         r = ext4_fs_free_inode(&current);
2127                                         if (r != EOK) {
2128                                                 ext4_fs_put_inode_ref(&parent);
2129                                                 goto End;
2130                                         }
2131                                 }
2132
2133                                 r = ext4_fs_put_inode_ref(&parent);
2134                                 if (r != EOK)
2135                                         goto End;
2136                         }
2137                 }
2138
2139         End:
2140                 ext4_dir_iterator_fini(&it);
2141                 ext4_fs_put_inode_ref(&current);
2142                 dir_end = false;
2143
2144                 /*When something goes wrong. End loop.*/
2145                 if (r != EOK)
2146                         break;
2147
2148         } while (depth);
2149
2150         ext4_block_cache_write_back(mp->fs.bdev, 0);
2151         EXT4_MP_UNLOCK(mp);
2152         return r;
2153 }
2154
2155 int ext4_dir_mk(const char *path)
2156 {
2157         int r;
2158         ext4_file f;
2159
2160         struct ext4_mountpoint *mp = ext4_get_mount(path);
2161
2162         if (!mp)
2163                 return ENOENT;
2164
2165         EXT4_MP_LOCK(mp);
2166
2167         /*Check if exist.*/
2168         r = ext4_generic_open(&f, path, "r", false, 0, 0);
2169         if (r == EOK) {
2170                 /*Directory already created*/
2171                 EXT4_MP_UNLOCK(mp);
2172                 return r;
2173         }
2174
2175         /*Create new dir*/
2176         r = ext4_generic_open(&f, path, "w", false, 0, 0);
2177         if (r != EOK) {
2178                 EXT4_MP_UNLOCK(mp);
2179                 return r;
2180         }
2181
2182         EXT4_MP_UNLOCK(mp);
2183         return r;
2184 }
2185
2186 int ext4_dir_open(ext4_dir *d, const char *path)
2187 {
2188         struct ext4_mountpoint *mp = ext4_get_mount(path);
2189         int r;
2190
2191         if (!mp)
2192                 return ENOENT;
2193
2194         EXT4_MP_LOCK(mp);
2195         r = ext4_generic_open(&d->f, path, "r", false, 0, 0);
2196         d->next_off = 0;
2197         EXT4_MP_UNLOCK(mp);
2198         return r;
2199 }
2200
2201 int ext4_dir_close(ext4_dir *d)
2202 {
2203     return ext4_fclose(&d->f);
2204 }
2205
2206 const ext4_direntry *ext4_dir_entry_next(ext4_dir *d)
2207 {
2208 #define EXT4_DIR_ENTRY_OFFSET_TERM (uint64_t)(-1)
2209
2210         int r;
2211         ext4_direntry *de = 0;
2212         struct ext4_inode_ref dir;
2213         struct ext4_directory_iterator it;
2214
2215         EXT4_MP_LOCK(d->f.mp);
2216
2217         if (d->next_off == EXT4_DIR_ENTRY_OFFSET_TERM) {
2218                 EXT4_MP_UNLOCK(d->f.mp);
2219                 return 0;
2220         }
2221
2222         r = ext4_fs_get_inode_ref(&d->f.mp->fs, d->f.inode, &dir);
2223         if (r != EOK) {
2224                 goto Finish;
2225         }
2226
2227         r = ext4_dir_iterator_init(&it, &dir, d->next_off);
2228         if (r != EOK) {
2229                 ext4_fs_put_inode_ref(&dir);
2230                 goto Finish;
2231         }
2232
2233         memcpy(&d->de, it.current, sizeof(ext4_direntry));
2234         de = &d->de;
2235
2236         ext4_dir_iterator_next(&it);
2237
2238         d->next_off =
2239             it.current ? it.current_offset : EXT4_DIR_ENTRY_OFFSET_TERM;
2240
2241         ext4_dir_iterator_fini(&it);
2242         ext4_fs_put_inode_ref(&dir);
2243
2244 Finish:
2245         EXT4_MP_UNLOCK(d->f.mp);
2246         return de;
2247 }
2248
2249 void ext4_dir_entry_rewind(ext4_dir *d)
2250 {
2251     d->next_off = 0;
2252 }
2253
2254 /**
2255  * @}
2256  */