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