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