2 * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
29 /** @addtogroup lwext4
34 * @brief Ext4 high level operations (file, directory, mountpoints...)
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"
44 #include "ext4_inode.h"
45 #include "ext4_super.h"
46 #include "ext4_dir_idx.h"
52 /**@brief Mount point OS dependent lock*/
53 #define EXT4_MP_LOCK(_m) \
56 (_m)->os_locks->lock(); \
59 /**@brief Mount point OS dependent unlock*/
60 #define EXT4_MP_UNLOCK(_m) \
63 (_m)->os_locks->unlock(); \
66 /**@brief Mount point descriptor.*/
67 struct ext4_mountpoint {
69 /**@brief Mount done flag.*/
72 /**@brief Mount point name (@ref ext4_mount)*/
75 /**@brief OS dependent lock/unlock functions.*/
76 const struct ext4_lock *os_locks;
78 /**@brief Ext4 filesystem internals.*/
81 /**@brief Dynamic allocation cache flag.*/
85 /**@brief Block devices descriptor.*/
86 struct _ext4_devices {
88 /**@brief Block device name (@ref ext4_device_register)*/
91 /**@brief Block device handle.*/
92 struct ext4_blockdev *bd;
94 /**@brief Block cache handle.*/
95 struct ext4_bcache *bc;
98 /**@brief Block devices.*/
99 struct _ext4_devices _bdevices[CONFIG_EXT4_BLOCKDEVS_COUNT];
101 /**@brief Mountpoints.*/
102 struct ext4_mountpoint _mp[CONFIG_EXT4_MOUNTPOINTS_COUNT];
104 int ext4_device_register(struct ext4_blockdev *bd, struct ext4_bcache *bc,
105 const char *dev_name)
108 ext4_assert(bd && dev_name);
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;
118 if (!strcmp(_bdevices[i].name, dev_name))
124 /****************************************************************************/
126 static bool ext4_is_dots(const uint8_t *name, size_t name_size)
128 if ((name_size == 1) && (name[0] == '.'))
131 if ((name_size == 2) && (name[0] == '.') && (name[1] == '.'))
137 static int ext4_has_children(bool *has_children, struct ext4_inode_ref *enode)
139 struct ext4_fs *fs = enode->fs;
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;
148 struct ext4_directory_iterator it;
149 int rc = ext4_dir_iterator_init(&it, enode, 0);
153 /* Find a non-empty directory entry */
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)) {
165 rc = ext4_dir_iterator_next(&it);
167 ext4_dir_iterator_fini(&it);
172 rc = ext4_dir_iterator_fini(&it);
176 *has_children = found;
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)
185 /* Check maximum name length */
186 if (name_len > EXT4_DIRECTORY_FILENAME_LEN)
189 /* Add entry to parent directory */
190 int rc = ext4_dir_add_entry(parent, name, name_len, child);
194 /* Fill new dir -> add '.' and '..' entries.
195 * Also newly allocated inode should have 0 link count.
197 if (ext4_inode_is_type(&mp->fs.sb, child->inode,
198 EXT4_INODE_MODE_DIRECTORY) &&
200 rc = ext4_dir_add_entry(child, ".", strlen("."), child);
202 ext4_dir_remove_entry(parent, name, strlen(name));
206 rc = ext4_dir_add_entry(child, "..", strlen(".."), parent);
208 ext4_dir_remove_entry(parent, name, strlen(name));
209 ext4_dir_remove_entry(child, ".", strlen("."));
213 /*New empty directory. Two links (. and ..) */
214 ext4_inode_set_links_count(child->inode, 2);
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);
224 ext4_inode_set_flag(child->inode,
225 EXT4_INODE_FLAG_INDEX);
230 ext4_fs_inode_links_count_inc(parent);
232 parent->dirty = true;
235 * In case we want to rename a directory,
236 * we reset the original '..' pointer.
238 if (ext4_inode_is_type(&mp->fs.sb, child->inode,
239 EXT4_INODE_MODE_DIRECTORY)) {
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,
251 ext4_dir_entry_ll_set_inode(result.dentry,
253 result.block.dirty = true;
254 rc = ext4_dir_destroy_result(child, &result);
259 #if CONFIG_DIR_INDEX_ENABLE
260 rc = ext4_dir_dx_reset_parent_inode(child,
268 ext4_fs_inode_links_count_inc(parent);
269 parent->dirty = true;
272 ext4_fs_inode_links_count_inc(child);
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,
286 int rc = ext4_has_children(&has_children, child_inode_ref);
290 /* Cannot unlink non-empty node */
294 /* Remove entry from parent directory */
295 rc = ext4_dir_remove_entry(parent, name, name_len);
299 bool is_dir = ext4_inode_is_type(&mp->fs.sb, child_inode_ref->inode,
300 EXT4_INODE_MODE_DIRECTORY);
302 /* If directory - handle links from parent */
304 // ext4_assert(ext4_inode_get_links_count(child_inode_ref->inode)
306 ext4_fs_inode_links_count_dec(parent);
307 parent->dirty = true;
311 * TODO: Update timestamps of the parent
312 * (when we have wall-clock time).
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;
320 * TODO: Update timestamp for inode.
322 * ext4_inode_set_change_inode_time(child_inode_ref->inode,
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;
333 /****************************************************************************/
335 int ext4_mount(const char *dev_name, const char *mount_point)
337 ext4_assert(mount_point && dev_name);
342 struct ext4_blockdev *bd = 0;
343 struct ext4_bcache *bc = 0;
344 struct ext4_mountpoint *mp = 0;
346 if (mount_point[strlen(mount_point) - 1] != '/')
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;
362 for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
363 if (!_mp[i].mounted) {
364 strcpy(_mp[i].name, mount_point);
370 if (!strcmp(_mp[i].name, mount_point))
377 r = ext4_block_init(bd);
381 r = ext4_fs_init(&mp->fs, bd);
387 bsize = ext4_sb_get_block_size(&mp->fs.sb);
388 ext4_block_set_lb_size(bd, bsize);
390 mp->cache_dynamic = 0;
393 /*Automatic block cache alloc.*/
394 mp->cache_dynamic = 1;
395 bc = malloc(sizeof(struct ext4_bcache));
397 r = ext4_bcache_init_dynamic(bc, CONFIG_BLOCK_DEV_CACHE_SIZE,
406 if (bsize != bc->itemsize)
409 /*Bind block cache to block device*/
410 r = ext4_block_bind_bcache(bd, bc);
413 if (mp->cache_dynamic) {
414 ext4_bcache_fini_dynamic(bc);
423 int ext4_umount(const char *mount_point)
427 struct ext4_mountpoint *mp = 0;
429 for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
430 if (!strcmp(_mp[i].name, mount_point)) {
439 r = ext4_fs_fini(&mp->fs);
445 if (mp->cache_dynamic) {
446 ext4_bcache_fini_dynamic(mp->fs.bdev->bc);
447 free(mp->fs.bdev->bc);
450 return ext4_block_fini(mp->fs.bdev);
453 int ext4_mount_point_stats(const char *mount_point,
454 struct ext4_mount_stats *stats)
457 struct ext4_mountpoint *mp = 0;
459 for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
460 if (!strcmp(_mp[i].name, mount_point)) {
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);
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);
479 memcpy(stats->volume_name, mp->fs.sb.volume_name, 16);
485 int ext4_mount_setup_locks(const char *mount_point,
486 const struct ext4_lock *locks)
489 struct ext4_mountpoint *mp = 0;
491 for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
492 if (!strcmp(_mp[i].name, mount_point)) {
500 mp->os_locks = locks;
504 /********************************FILE OPERATIONS*****************************/
506 static struct ext4_mountpoint *ext4_get_mount(const char *path)
509 for (i = 0; i < CONFIG_EXT4_MOUNTPOINTS_COUNT; ++i) {
514 if (!strncmp(_mp[i].name, path, strlen(_mp[i].name)))
520 static int ext4_path_check(const char *path, bool *is_goal)
524 for (i = 0; i < EXT4_DIRECTORY_FILENAME_LEN; ++i) {
526 if (path[i] == '/') {
540 static bool ext4_parse_flags(const char *flags, uint32_t *file_flags)
545 if (!strcmp(flags, "r") || !strcmp(flags, "rb")) {
546 *file_flags = O_RDONLY;
550 if (!strcmp(flags, "w") || !strcmp(flags, "wb")) {
551 *file_flags = O_WRONLY | O_CREAT | O_TRUNC;
555 if (!strcmp(flags, "a") || !strcmp(flags, "ab")) {
556 *file_flags = O_WRONLY | O_CREAT | O_APPEND;
560 if (!strcmp(flags, "r+") || !strcmp(flags, "rb+") ||
561 !strcmp(flags, "r+b")) {
562 *file_flags = O_RDWR;
566 if (!strcmp(flags, "w+") || !strcmp(flags, "wb+") ||
567 !strcmp(flags, "w+b")) {
568 *file_flags = O_RDWR | O_CREAT | O_TRUNC;
572 if (!strcmp(flags, "a+") || !strcmp(flags, "ab+") ||
573 !strcmp(flags, "a+b")) {
574 *file_flags = O_RDWR | O_CREAT | O_APPEND;
582 * NOTICE: if filetype is equal to EXT4_DIRENTRY_UNKNOWN,
583 * any filetype of the target dir entry will be accepted.
585 static int ext4_generic_open2(ext4_file *f, const char *path, int flags,
586 int filetype, uint32_t *parent_inode,
589 bool is_goal = false;
590 uint8_t inode_type = EXT4_DIRENTRY_UNKNOWN;
594 struct ext4_mountpoint *mp = ext4_get_mount(path);
595 struct ext4_directory_search_result result;
596 struct ext4_inode_ref ref;
606 path += strlen(mp->name);
609 *name_off = strlen(mp->name);
612 r = ext4_fs_get_inode_ref(&mp->fs, EXT4_INODE_ROOT_INDEX, &ref);
618 *parent_inode = ref.index;
620 int len = ext4_path_check(path, &is_goal);
624 len = ext4_path_check(path, &is_goal);
627 /*If root open was request.*/
629 ((filetype == EXT4_DIRENTRY_DIR) ||
630 (filetype == EXT4_DIRENTRY_UNKNOWN)))
637 r = ext4_dir_find_entry(&result, &ref, path, len);
640 /*Destroy last result*/
641 ext4_dir_destroy_result(&ref, &result);
646 if (!(f->flags & O_CREAT))
649 /*O_CREAT allows create new entry*/
650 struct ext4_inode_ref child_ref;
651 r = ext4_fs_alloc_inode(
654 : EXT4_DIRENTRY_DIR);
659 /*Link with root dir.*/
660 r = ext4_link(mp, &ref, &child_ref, path, len, false);
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);
671 ext4_fs_put_inode_ref(&child_ref);
677 *parent_inode = ref.index;
679 next_inode = ext4_dir_entry_ll_get_inode(result.dentry);
681 ext4_dir_entry_ll_get_inode_type(&mp->fs.sb, result.dentry);
683 r = ext4_dir_destroy_result(&ref, &result);
687 /*If expected file error*/
688 if (inode_type != EXT4_DIRENTRY_DIR && !is_goal) {
692 if (filetype != EXT4_DIRENTRY_UNKNOWN) {
693 if ((inode_type != filetype) && is_goal) {
699 r = ext4_fs_put_inode_ref(&ref);
703 r = ext4_fs_get_inode_ref(&mp->fs, next_inode, &ref);
713 *name_off += len + 1;
717 ext4_fs_put_inode_ref(&ref);
723 if ((f->flags & O_TRUNC) &&
724 (inode_type == EXT4_DIRENTRY_REG_FILE)) {
726 r = ext4_fs_truncate_inode(&ref, 0);
728 ext4_fs_put_inode_ref(&ref);
734 f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);
735 f->inode = ref.index;
738 if (f->flags & O_APPEND)
742 r = ext4_fs_put_inode_ref(&ref);
746 /****************************************************************************/
748 static int ext4_generic_open(ext4_file *f, const char *path, const char *flags,
749 bool file_expect, uint32_t *parent_inode,
754 if (ext4_parse_flags(flags, &iflags) == false)
757 if (file_expect == true)
758 filetype = EXT4_DIRENTRY_REG_FILE;
760 filetype = EXT4_DIRENTRY_DIR;
762 return ext4_generic_open2(f, path, iflags, filetype, parent_inode,
766 static int __ext4_create_hardlink(const char *path,
767 struct ext4_inode_ref *child_ref,
770 bool is_goal = false;
771 uint8_t inode_type = EXT4_DIRENTRY_DIR;
775 struct ext4_mountpoint *mp = ext4_get_mount(path);
776 struct ext4_directory_search_result result;
777 struct ext4_inode_ref ref;
783 path += strlen(mp->name);
786 r = ext4_fs_get_inode_ref(&mp->fs, EXT4_INODE_ROOT_INDEX, &ref);
791 int len = ext4_path_check(path, &is_goal);
795 len = ext4_path_check(path, &is_goal);
798 /*If root open was request.*/
806 r = ext4_dir_find_entry(&result, &ref, path, len);
809 /*Destroy last result*/
810 ext4_dir_destroy_result(&ref, &result);
812 if (r != ENOENT || !is_goal)
815 /*Link with root dir.*/
816 r = ext4_link(mp, &ref, child_ref, path, len, rename);
818 } else if (r == EOK && is_goal) {
819 /*Destroy last result*/
820 ext4_dir_destroy_result(&ref, &result);
825 next_inode = result.dentry->inode;
827 ext4_dir_entry_ll_get_inode_type(&mp->fs.sb, result.dentry);
829 r = ext4_dir_destroy_result(&ref, &result);
833 if (inode_type == EXT4_DIRENTRY_REG_FILE) {
842 r = ext4_fs_put_inode_ref(&ref);
846 r = ext4_fs_get_inode_ref(&mp->fs, next_inode, &ref);
857 ext4_fs_put_inode_ref(&ref);
861 r = ext4_fs_put_inode_ref(&ref);
865 static int ext4_remove_orig_reference(const char *path,
867 struct ext4_inode_ref *parent_ref,
868 struct ext4_inode_ref *child_ref)
873 struct ext4_mountpoint *mp = ext4_get_mount(path);
881 len = ext4_path_check(path, &is_goal);
883 /* Remove entry from parent directory */
884 r = ext4_dir_remove_entry(parent_ref, path, len);
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;
897 int ext4_flink(const char *path, const char *hardlink_path)
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;
911 /* Will that happen? Anyway return EINVAL for such case. */
917 r = ext4_generic_open2(&f, path, O_RDONLY,
918 EXT4_DIRENTRY_UNKNOWN,
919 &parent_inode, &name_off);
923 child_inode = f.inode;
926 /*We have file to unlink. Load it.*/
927 r = ext4_fs_get_inode_ref(&mp->fs, child_inode, &child_ref);
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)) {
940 r = __ext4_create_hardlink(hardlink_path, &child_ref, false);
944 ext4_fs_put_inode_ref(&child_ref);
951 int ext4_frename(const char *path, const char *new_path)
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;
966 r = ext4_generic_open2(&f, path, O_RDONLY,
967 EXT4_DIRENTRY_UNKNOWN,
968 &parent_inode, &name_off);
972 child_inode = f.inode;
976 r = ext4_fs_get_inode_ref(&mp->fs, parent_inode, &parent_ref);
980 parent_loaded = true;
982 /*We have file to unlink. Load it.*/
983 r = ext4_fs_get_inode_ref(&mp->fs, child_inode, &child_ref);
989 r = __ext4_create_hardlink(new_path, &child_ref, true);
993 r = ext4_remove_orig_reference(path, name_off,
994 &parent_ref, &child_ref);
1000 ext4_fs_put_inode_ref(&parent_ref);
1003 ext4_fs_put_inode_ref(&child_ref);
1010 /****************************************************************************/
1012 int ext4_get_sblock(const char *mount_point, struct ext4_sblock **sb)
1014 struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
1023 int ext4_cache_write_back(const char *path, bool on)
1025 struct ext4_mountpoint *mp = ext4_get_mount(path);
1031 ext4_block_cache_write_back(mp->fs.bdev, on);
1036 int ext4_fremove(const char *path)
1039 uint32_t parent_inode;
1044 struct ext4_inode_ref child;
1045 struct ext4_inode_ref parent;
1046 struct ext4_mountpoint *mp = ext4_get_mount(path);
1052 r = ext4_generic_open2(&f, path, O_RDWR,
1053 EXT4_DIRENTRY_UNKNOWN,
1054 &parent_inode, &name_off);
1061 r = ext4_fs_get_inode_ref(&mp->fs, parent_inode, &parent);
1067 /*We have file to delete. Load it.*/
1068 r = ext4_fs_get_inode_ref(&mp->fs, f.inode, &child);
1070 ext4_fs_put_inode_ref(&parent);
1078 len = ext4_path_check(path, &is_goal);
1080 /*Unlink from parent*/
1081 r = ext4_unlink(mp, &parent, &child, path, len);
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);
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);
1097 r = ext4_fs_free_inode(&child);
1103 ext4_fs_put_inode_ref(&child);
1104 ext4_fs_put_inode_ref(&parent);
1109 int ext4_fill_raw_inode(const char *path,
1111 struct ext4_inode *inode)
1115 struct ext4_inode_ref inode_ref;
1116 struct ext4_mountpoint *mp = ext4_get_mount(path);
1124 r = ext4_generic_open2(&f, path, O_RDONLY,
1125 EXT4_DIRENTRY_UNKNOWN,
1136 r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
1142 memcpy(inode, inode_ref.inode, sizeof(struct ext4_inode));
1144 ext4_fs_put_inode_ref(&inode_ref);
1153 int ext4_fopen(ext4_file *f, const char *path, const char *flags)
1155 struct ext4_mountpoint *mp = ext4_get_mount(path);
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);
1169 int ext4_fopen2(ext4_file *f, const char *path, int flags)
1171 struct ext4_mountpoint *mp = ext4_get_mount(path);
1178 filetype = EXT4_DIRENTRY_REG_FILE;
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);
1188 int ext4_fclose(ext4_file *f)
1190 ext4_assert(f && f->mp);
1195 f->fpos = f->fsize = 0;
1200 static int ext4_ftruncate_no_lock(ext4_file *f, uint64_t size)
1202 struct ext4_inode_ref ref;
1206 r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
1208 EXT4_MP_UNLOCK(f->mp);
1213 f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);
1214 if (f->fsize <= size) {
1219 /*Start write back cache mode.*/
1220 r = ext4_block_cache_write_back(f->mp->fs.bdev, 1);
1224 r = ext4_fs_truncate_inode(&ref, size);
1232 /*Stop write back cache mode*/
1233 ext4_block_cache_write_back(f->mp->fs.bdev, 0);
1239 ext4_fs_put_inode_ref(&ref);
1244 int ext4_ftruncate(ext4_file *f, uint64_t size)
1247 ext4_assert(f && f->mp);
1249 if (f->flags & O_RDONLY)
1252 EXT4_MP_LOCK(f->mp);
1254 r = ext4_ftruncate_no_lock(f, size);
1256 EXT4_MP_UNLOCK(f->mp);
1260 int ext4_fread(ext4_file *f, void *buf, size_t size, size_t *rcnt)
1264 uint32_t fblock_start;
1265 uint32_t fblock_cnt;
1267 uint32_t sblock_end;
1268 uint32_t block_size;
1269 uint8_t *u8_buf = buf;
1271 struct ext4_block b;
1272 struct ext4_inode_ref ref;
1274 ext4_assert(f && f->mp);
1276 if (f->flags & O_WRONLY)
1282 EXT4_MP_LOCK(f->mp);
1287 r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
1289 EXT4_MP_UNLOCK(f->mp);
1294 f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);
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;
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)
1311 memcpy(buf, content + u, r);
1328 uint32_t ll = size > (block_size - u) ? (block_size - u) : size;
1330 r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);
1334 r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
1338 memcpy(u8_buf, b.data + u, ll);
1340 r = ext4_block_set(f->mp->fs.bdev, &b);
1356 while (size >= block_size) {
1357 while (sblock < sblock_end) {
1358 r = ext4_fs_get_inode_data_block_index(&ref, sblock,
1365 if (!fblock_start) {
1366 fblock_start = fblock;
1369 if ((fblock_start + fblock_cnt) != fblock)
1375 r = ext4_blocks_get_direct(f->mp->fs.bdev, u8_buf, fblock_start,
1380 size -= block_size * fblock_cnt;
1381 u8_buf += block_size * fblock_cnt;
1382 f->fpos += block_size * fblock_cnt;
1385 *rcnt += block_size * fblock_cnt;
1387 fblock_start = fblock;
1392 r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);
1396 r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
1400 memcpy(u8_buf, b.data, size);
1402 r = ext4_block_set(f->mp->fs.bdev, &b);
1413 ext4_fs_put_inode_ref(&ref);
1414 EXT4_MP_UNLOCK(f->mp);
1418 int ext4_fwrite(ext4_file *f, const void *buf, size_t size, size_t *wcnt)
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;
1430 struct ext4_block b;
1431 struct ext4_inode_ref ref;
1432 const uint8_t *u8_buf = buf;
1435 ext4_assert(f && f->mp);
1437 if (f->flags & O_RDONLY)
1443 EXT4_MP_LOCK(f->mp);
1448 r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
1450 EXT4_MP_UNLOCK(f->mp);
1455 f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);
1457 block_size = ext4_sb_get_block_size(&f->mp->fs.sb);
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);
1463 if (f->fsize % block_size)
1466 sblock = (f->fpos) / block_size;
1468 u = (f->fpos) % block_size;
1471 uint32_t ll = size > (block_size - u) ? (block_size - u) : size;
1473 r = ext4_fs_get_inode_data_block_index(&ref, sblock, &fblock);
1477 r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
1481 memcpy(b.data + u, u8_buf, ll);
1484 r = ext4_block_set(f->mp->fs.bdev, &b);
1498 /*Start write back cache mode.*/
1499 r = ext4_block_cache_write_back(f->mp->fs.bdev, 1);
1505 while (size >= block_size) {
1507 while (sblock < sblock_end) {
1508 if (sblock < file_blocks) {
1509 r = ext4_fs_get_inode_data_block_index(
1510 &ref, sblock, &fblock);
1514 r = ext4_fs_append_inode_block(&ref, &fblock,
1522 if (!fblock_start) {
1523 fblock_start = fblock;
1526 if ((fblock_start + fblock_cnt) != fblock)
1532 r = ext4_blocks_set_direct(f->mp->fs.bdev, u8_buf, fblock_start,
1537 size -= block_size * fblock_cnt;
1538 u8_buf += block_size * fblock_cnt;
1539 f->fpos += block_size * fblock_cnt;
1542 *wcnt += block_size * fblock_cnt;
1544 fblock_start = fblock;
1548 /*Stop write back cache mode*/
1549 ext4_block_cache_write_back(f->mp->fs.bdev, 0);
1555 if (sblock < file_blocks) {
1556 r = ext4_fs_get_inode_data_block_index(&ref, sblock,
1561 r = ext4_fs_append_inode_block(&ref, &fblock, &sblock);
1566 r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
1570 memcpy(b.data, u8_buf, size);
1573 r = ext4_block_set(f->mp->fs.bdev, &b);
1583 if (f->fpos > f->fsize) {
1585 ext4_inode_set_size(ref.inode, f->fsize);
1590 ext4_fs_put_inode_ref(&ref);
1591 EXT4_MP_UNLOCK(f->mp);
1595 int ext4_fseek(ext4_file *f, uint64_t offset, uint32_t origin)
1599 if (offset > f->fsize)
1605 if ((offset + f->fpos) > f->fsize)
1611 if (offset > f->fsize)
1614 f->fpos = f->fsize - offset;
1620 uint64_t ext4_ftell(ext4_file *f) { return f->fpos; }
1622 uint64_t ext4_fsize(ext4_file *f) { return f->fsize; }
1624 int ext4_chmod(const char *path, uint32_t mode)
1629 struct ext4_sblock *sb;
1630 struct ext4_inode_ref inode_ref;
1631 struct ext4_mountpoint *mp = ext4_get_mount(path);
1638 r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DIRENTRY_UNKNOWN, 0, 0);
1646 r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
1652 ext4_inode_set_mode(sb, inode_ref.inode, mode);
1653 inode_ref.dirty = true;
1655 ext4_fs_put_inode_ref(&inode_ref);
1660 int ext4_chown(const char *path, uint32_t uid, uint32_t gid)
1665 struct ext4_inode_ref inode_ref;
1666 struct ext4_mountpoint *mp = ext4_get_mount(path);
1673 r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DIRENTRY_UNKNOWN, 0, 0);
1680 r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
1686 ext4_inode_set_uid(inode_ref.inode, uid);
1687 ext4_inode_set_gid(inode_ref.inode, gid);
1688 inode_ref.dirty = true;
1690 ext4_fs_put_inode_ref(&inode_ref);
1695 int ext4_file_set_atime(const char *path, uint32_t atime)
1700 struct ext4_inode_ref inode_ref;
1701 struct ext4_mountpoint *mp = ext4_get_mount(path);
1708 r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DIRENTRY_UNKNOWN, 0, 0);
1715 r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
1721 ext4_inode_set_access_time(inode_ref.inode, atime);
1722 inode_ref.dirty = true;
1724 ext4_fs_put_inode_ref(&inode_ref);
1729 int ext4_file_set_mtime(const char *path, uint32_t mtime)
1734 struct ext4_inode_ref inode_ref;
1735 struct ext4_mountpoint *mp = ext4_get_mount(path);
1742 r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DIRENTRY_UNKNOWN, 0, 0);
1749 r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
1755 ext4_inode_set_modification_time(inode_ref.inode, mtime);
1756 inode_ref.dirty = true;
1758 ext4_fs_put_inode_ref(&inode_ref);
1763 int ext4_file_set_ctime(const char *path, uint32_t ctime)
1768 struct ext4_inode_ref inode_ref;
1769 struct ext4_mountpoint *mp = ext4_get_mount(path);
1776 r = ext4_generic_open2(&f, path, O_RDWR, EXT4_DIRENTRY_UNKNOWN, 0, 0);
1783 r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
1789 ext4_inode_set_change_inode_time(inode_ref.inode, ctime);
1790 inode_ref.dirty = true;
1792 ext4_fs_put_inode_ref(&inode_ref);
1797 static int ext4_fsymlink_set(ext4_file *f, const void *buf, uint32_t size)
1799 struct ext4_block b;
1800 struct ext4_inode_ref ref;
1801 uint32_t sblock, fblock;
1802 uint32_t block_size;
1805 ext4_assert(f && f->mp);
1810 r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
1812 EXT4_MP_UNLOCK(f->mp);
1817 block_size = ext4_sb_get_block_size(&f->mp->fs.sb);
1818 if (size > block_size) {
1822 r = ext4_ftruncate_no_lock(f, 0);
1826 /*Start write back cache mode.*/
1827 r = ext4_block_cache_write_back(f->mp->fs.bdev, 1);
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);
1838 ext4_fs_inode_blocks_init(&f->mp->fs, &ref);
1839 r = ext4_fs_append_inode_block(&ref, &fblock, &sblock);
1843 r = ext4_block_get(f->mp->fs.bdev, &b, fblock);
1847 memcpy(b.data, buf, size);
1849 r = ext4_block_set(f->mp->fs.bdev, &b);
1854 /*Stop write back cache mode*/
1855 ext4_block_cache_write_back(f->mp->fs.bdev, 0);
1860 ext4_inode_set_size(ref.inode, size);
1868 ext4_fs_put_inode_ref(&ref);
1872 int ext4_fsymlink(const char *target, const char *path)
1874 struct ext4_mountpoint *mp = ext4_get_mount(path);
1882 filetype = EXT4_DIRENTRY_SYMLINK;
1885 ext4_block_cache_write_back(mp->fs.bdev, 1);
1886 r = ext4_generic_open2(&f, path, O_RDWR|O_CREAT, filetype, 0, 0);
1888 r = ext4_fsymlink_set(&f, target, strlen(target));
1895 ext4_block_cache_write_back(mp->fs.bdev, 0);
1900 int ext4_readlink(const char *path, char *buf, size_t bufsize, size_t *rcnt)
1902 struct ext4_mountpoint *mp = ext4_get_mount(path);
1913 memset(buf, 0, sizeof(bufsize));
1915 filetype = EXT4_DIRENTRY_SYMLINK;
1918 ext4_block_cache_write_back(mp->fs.bdev, 1);
1919 r = ext4_generic_open2(&f, path, O_RDONLY, filetype, 0, 0);
1921 r = ext4_fread(&f, buf, bufsize, rcnt);
1928 ext4_block_cache_write_back(mp->fs.bdev, 0);
1933 /*********************************DIRECTORY OPERATION************************/
1935 int ext4_dir_rm(const char *path)
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;
1948 uint32_t inode_current;
1961 r = ext4_generic_open(&f, path, "r", false, &inode_up, &name_off);
1968 len = ext4_path_check(path, &is_goal);
1970 inode_current = f.inode;
1973 ext4_block_cache_write_back(mp->fs.bdev, 1);
1976 /*Load directory node.*/
1977 r = ext4_fs_get_inode_ref(&f.mp->fs, inode_current, ¤t);
1982 /*Initialize iterator.*/
1983 r = ext4_dir_iterator_init(&it, ¤t, 0);
1985 ext4_fs_put_inode_ref(¤t);
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);
2003 /*If directory or file entry, but not "." ".." entry*/
2004 if (!ext4_is_dots(it.current->name,
2005 it.current->name_length)) {
2007 /*Get child inode reference do unlink
2009 r = ext4_fs_get_inode_ref(&f.mp->fs,
2010 ext4_dir_entry_ll_get_inode(it.current),
2015 /*If directory with no leaf children*/
2016 r = ext4_has_children(&has_children, &child);
2018 ext4_fs_put_inode_ref(&child);
2023 /*Has directory children. Go into this
2025 inode_up = inode_current;
2026 inode_current = ext4_dir_entry_ll_get_inode(it.current);
2028 ext4_fs_put_inode_ref(&child);
2032 /*No children in child directory or file. Just
2034 r = ext4_unlink(f.mp, ¤t, &child,
2035 (char *)it.current->name,
2036 it.current->name_length);
2038 ext4_fs_put_inode_ref(&child);
2042 ext4_inode_set_deletion_time(child.inode,
2044 ext4_inode_set_links_count(child.inode, 0);
2047 r = ext4_fs_truncate_inode(&child, 0);
2049 ext4_fs_put_inode_ref(&child);
2053 r = ext4_fs_free_inode(&child);
2055 ext4_fs_put_inode_ref(&child);
2059 r = ext4_fs_put_inode_ref(&child);
2064 r = ext4_dir_iterator_next(&it);
2068 /*Directory iterator reached last entry*/
2069 ext4_has_children(&has_children, ¤t);
2070 if (!has_children) {
2071 inode_current = inode_up;
2078 struct ext4_inode_ref parent;
2079 r = ext4_fs_get_inode_ref(&f.mp->fs, inode_up,
2084 /* In this place all directories should be
2086 * Last unlink from root of current directory*/
2087 r = ext4_unlink(f.mp, &parent, ¤t,
2090 ext4_fs_put_inode_ref(&parent);
2094 if (ext4_inode_get_links_count(current.inode) ==
2096 ext4_inode_set_deletion_time(
2097 current.inode, 0xFFFFFFFF);
2098 ext4_inode_set_links_count(
2100 current.dirty = true;
2102 r = ext4_fs_truncate_inode(¤t, 0);
2104 ext4_fs_put_inode_ref(&parent);
2108 r = ext4_fs_free_inode(¤t);
2110 ext4_fs_put_inode_ref(&parent);
2115 r = ext4_fs_put_inode_ref(&parent);
2122 ext4_dir_iterator_fini(&it);
2123 ext4_fs_put_inode_ref(¤t);
2126 /*When something goes wrong. End loop.*/
2132 ext4_block_cache_write_back(mp->fs.bdev, 0);
2137 int ext4_dir_mk(const char *path)
2142 struct ext4_mountpoint *mp = ext4_get_mount(path);
2150 r = ext4_generic_open(&f, path, "r", false, 0, 0);
2152 /*Directory already created*/
2158 r = ext4_generic_open(&f, path, "w", false, 0, 0);
2168 int ext4_dir_open(ext4_dir *d, const char *path)
2170 struct ext4_mountpoint *mp = ext4_get_mount(path);
2177 r = ext4_generic_open(&d->f, path, "r", false, 0, 0);
2183 int ext4_dir_close(ext4_dir *d) { return ext4_fclose(&d->f); }
2185 const ext4_direntry *ext4_dir_entry_next(ext4_dir *d)
2187 #define EXT4_DIR_ENTRY_OFFSET_TERM (uint64_t)(-1)
2190 ext4_direntry *de = 0;
2191 struct ext4_inode_ref dir;
2192 struct ext4_directory_iterator it;
2194 EXT4_MP_LOCK(d->f.mp);
2196 if (d->next_off == EXT4_DIR_ENTRY_OFFSET_TERM) {
2197 EXT4_MP_UNLOCK(d->f.mp);
2201 r = ext4_fs_get_inode_ref(&d->f.mp->fs, d->f.inode, &dir);
2206 r = ext4_dir_iterator_init(&it, &dir, d->next_off);
2208 ext4_fs_put_inode_ref(&dir);
2212 memcpy(&d->de, it.current, sizeof(ext4_direntry));
2215 ext4_dir_iterator_next(&it);
2218 it.current ? it.current_offset : EXT4_DIR_ENTRY_OFFSET_TERM;
2220 ext4_dir_iterator_fini(&it);
2221 ext4_fs_put_inode_ref(&dir);
2224 EXT4_MP_UNLOCK(d->f.mp);