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