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