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