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