shithub: lwext4

Download patch

ref: 2a1d5f53b73e5626183a02d2a2af9567bd3e012e
parent: 3d1db93236f9cac2ea0103e696633359785ef514
author: gkostka <kostka.grzegorz@gmail.com>
date: Wed Sep 16 19:04:25 EDT 2015

ngkaho1234: lwext4 improvments & bugfixes

--- a/lwext4/CMakeLists.txt
+++ b/lwext4/CMakeLists.txt
@@ -3,6 +3,6 @@
 include_directories(.)
 aux_source_directory(. LWEXT4_SRC)
 add_library(lwext4  ${LWEXT4_SRC})
-add_custom_target(lib_size ALL DEPENDS lwext4 COMMAND ${SIZE} -B liblwext4.a)
+add_custom_target(lib_size ALL DEPENDS lwext4 COMMAND ${SIZE} liblwext4.a)
 
 
--- a/lwext4/ext4.c
+++ b/lwext4/ext4.c
@@ -190,9 +190,12 @@
     if (rc != EOK)
         return rc;
 
-    /* Fill new dir -> add '.' and '..' entries */
+    /* Fill new dir -> add '.' and '..' entries.
+     * Also newly allocated inode should have 0 link count.
+     */
     if (ext4_inode_is_type(&mp->fs.sb, child->inode,
-                           EXT4_INODE_MODE_DIRECTORY)) {
+                           EXT4_INODE_MODE_DIRECTORY)
+        && ext4_inode_get_links_count(child->inode) == 0) {
         rc = ext4_dir_add_entry(child, ".", strlen("."), child);
         if (rc != EOK) {
             ext4_dir_remove_entry(parent, name, strlen(name));
@@ -225,9 +228,38 @@
         ext4_fs_inode_links_count_inc(parent);
         child->dirty = true;
         parent->dirty = true;
+    } else {
+        if (ext4_inode_is_type(&mp->fs.sb, child->inode,
+                               EXT4_INODE_MODE_DIRECTORY)) {
+            /* FIXME: SO TRICKY. */
+            int has_flag_index =
+                    ext4_inode_has_flag(child->inode, EXT4_INODE_FLAG_INDEX);
+            struct ext4_directory_search_result result;
+            if (has_flag_index)
+                ext4_inode_clear_flag(child->inode, EXT4_INODE_FLAG_INDEX);
+
+            rc = ext4_dir_find_entry(&result, child, "..", strlen(".."));
+            if (has_flag_index)
+                ext4_inode_set_flag(child->inode, EXT4_INODE_FLAG_INDEX);
+
+            if (rc != EOK)
+                return EIO;
+
+            ext4_dir_entry_ll_set_inode(result.dentry, parent->index);
+            result.block.dirty = true;
+            rc = ext4_dir_destroy_result(child, &result);
+            if (rc != EOK)
+                return rc;
+
+            ext4_fs_inode_links_count_inc(parent);
+            parent->dirty = true;
+        } else {
+            ext4_fs_inode_links_count_inc(child);
+            child->dirty = true;
+        }
     }
 
-    return EOK;
+    return rc;
 }
 
 static int ext4_unlink(struct ext4_mountpoint *mp,
@@ -274,9 +306,10 @@
      * ext4_inode_set_change_inode_time(child_inode_ref->inode,
      *     (uint32_t) now);
      */
-    ext4_inode_set_deletion_time(child_inode_ref->inode, 0xFFFFFFFF);
-    ext4_inode_set_links_count(child_inode_ref->inode, 0);
-    child_inode_ref->dirty = true;
+    if (ext4_inode_get_links_count(child_inode_ref->inode)) {
+        ext4_fs_inode_links_count_dec(child_inode_ref);
+        child_inode_ref->dirty = true;
+    }
 
     return EOK;
 }
@@ -528,10 +561,12 @@
     return false;
 }
 
-/****************************************************************************/
-
-static int ext4_generic_open(ext4_file *f, const char *path, const char *flags,
-                             bool file_expect, uint32_t *parent_inode,
+/*
+ * NOTICE: if filetype is equal to EXT4_DIRECTORY_FILETYPE_UNKNOWN,
+ * any filetype of the target dir entry will be accepted.
+ */
+static int ext4_generic_open2(ext4_file *f, const char *path, int flags,
+                             int filetype, uint32_t *parent_inode,
                              uint32_t *name_off)
 {
     bool is_goal = false;
@@ -548,8 +583,7 @@
     if (!mp)
         return ENOENT;
 
-    if (ext4_parse_flags(flags, &f->flags) == false)
-        return EINVAL;
+    f->flags = flags;
 
     /*Skip mount point*/
     path += strlen(mp->name);
@@ -574,7 +608,8 @@
 
         if (!len) {
             /*If root open was request.*/
-            if (is_goal && !file_expect)
+            if (is_goal && ((filetype == EXT4_DIRECTORY_FILETYPE_DIR)
+                            || (filetype == EXT4_DIRECTORY_FILETYPE_UNKNOWN)))
                 break;
 
             r = ENOENT;
@@ -593,7 +628,9 @@
             /*O_CREAT allows create new entry*/
             struct ext4_inode_ref child_ref;
             r = ext4_fs_alloc_inode(&mp->fs, &child_ref,
-                                    is_goal ? !file_expect : true);
+                                    is_goal
+                                      ? (filetype == EXT4_DIRECTORY_FILETYPE_DIR)
+                                      : true);
             if (r != EOK)
                 break;
 
@@ -629,17 +666,15 @@
             break;
 
         /*If expected file error*/
-        if ((inode_type == EXT4_DIRECTORY_FILETYPE_REG_FILE) && !file_expect &&
-            is_goal) {
+        if (inode_type != EXT4_DIRECTORY_FILETYPE_DIR && !is_goal) {
             r = ENOENT;
             break;
         }
-
-        /*If expected directory error*/
-        if ((inode_type == EXT4_DIRECTORY_FILETYPE_DIR) && file_expect &&
-            is_goal) {
-            r = ENOENT;
-            break;
+        if (filetype != EXT4_DIRECTORY_FILETYPE_UNKNOWN) {
+            if ((inode_type != filetype) && is_goal) {
+                r = ENOENT;
+                break;
+            }
         }
 
         r = ext4_fs_put_inode_ref(&ref);
@@ -691,6 +726,211 @@
 
 /****************************************************************************/
 
+static int ext4_generic_open(ext4_file *f, const char *path, const char *flags,
+                             bool file_expect, uint32_t *parent_inode,
+                             uint32_t *name_off)
+{
+    uint32_t iflags;
+    int filetype;
+    if (ext4_parse_flags(flags, &iflags) == false)
+        return EINVAL;
+
+    if (file_expect == true)
+        filetype = EXT4_DIRECTORY_FILETYPE_REG_FILE;
+    else
+        filetype = EXT4_DIRECTORY_FILETYPE_DIR;
+
+    return ext4_generic_open2(f, path, iflags, filetype,
+                              parent_inode, name_off);
+}
+
+static int __ext4_create_hardlink(const char *path,
+                             struct ext4_inode_ref *child_ref)
+{
+    bool is_goal = false;
+    uint8_t inode_type = EXT4_DIRECTORY_FILETYPE_DIR;
+    uint32_t next_inode;
+
+    int r;
+    struct ext4_mountpoint *mp = ext4_get_mount(path);
+    struct ext4_directory_search_result result;
+    struct ext4_inode_ref ref;
+
+    if (!mp)
+        return ENOENT;
+
+    /*Skip mount point*/
+    path += strlen(mp->name);
+
+    /*Load root*/
+    r = ext4_fs_get_inode_ref(&mp->fs, EXT4_INODE_ROOT_INDEX, &ref);
+
+    if (r != EOK)
+        return r;
+
+    int len = ext4_path_check(path, &is_goal);
+
+    while (1) {
+
+        len = ext4_path_check(path, &is_goal);
+
+        if (!len) {
+            /*If root open was request.*/
+            if (is_goal)
+                r = EINVAL;
+            else
+                r = ENOENT;
+            break;
+        }
+
+        r = ext4_dir_find_entry(&result, &ref, path, len);
+        if (r != EOK) {
+
+            if (r != ENOENT || !is_goal)
+                break;
+
+            /*Destroy last result*/
+            ext4_dir_destroy_result(&ref, &result);
+
+            /*Link with root dir.*/
+            r = ext4_link(mp, &ref, child_ref, path, len);
+            break;
+        }
+
+        next_inode = result.dentry->inode;
+        inode_type =
+            ext4_dir_entry_ll_get_inode_type(&mp->fs.sb, result.dentry);
+
+        r = ext4_dir_destroy_result(&ref, &result);
+        if (r != EOK)
+            break;
+
+        if (inode_type == EXT4_DIRECTORY_FILETYPE_REG_FILE) {
+            if (is_goal)
+                r = EEXIST;
+            else
+                r = ENOENT;
+
+            break;
+        }
+
+        r = ext4_fs_put_inode_ref(&ref);
+        if (r != EOK)
+            break;
+
+        r = ext4_fs_get_inode_ref(&mp->fs, next_inode, &ref);
+        if (r != EOK)
+            break;
+
+        if (is_goal)
+            break;
+
+        path += len + 1;
+    };
+
+    if (r != EOK) {
+        ext4_fs_put_inode_ref(&ref);
+        return r;
+    }
+
+    r = ext4_fs_put_inode_ref(&ref);
+    return r;
+}
+
+static int __ext4_get_inode_ref_remove_hardlink(const char *path, struct ext4_inode_ref *child)
+{
+    ext4_file f;
+    uint32_t parent_inode;
+    uint32_t name_off;
+    bool is_goal;
+    int r;
+    int len;
+    struct ext4_inode_ref parent;
+    struct ext4_mountpoint *mp = ext4_get_mount(path);
+
+    if (!mp)
+        return ENOENT;
+
+    r = ext4_generic_open2(&f, path, O_RDONLY,
+                           EXT4_DIRECTORY_FILETYPE_UNKNOWN,
+                           &parent_inode, &name_off);
+    if (r != EOK)
+        return r;
+
+    /*Load parent*/
+    r = ext4_fs_get_inode_ref(&mp->fs, parent_inode, &parent);
+    if (r != EOK) {
+        return r;
+    }
+
+    /*We have file to unlink. Load it.*/
+    r = ext4_fs_get_inode_ref(&mp->fs, f.inode, child);
+    if (r != EOK) {
+        ext4_fs_put_inode_ref(&parent);
+        return r;
+    }
+
+    if (r != EOK)
+        goto Finish;
+
+    /*Set path*/
+    path += name_off;
+
+    len = ext4_path_check(path, &is_goal);
+
+    /*Unlink from parent*/
+    r = ext4_unlink(mp, &parent, child, path, len);
+    if (r != EOK)
+        goto Finish;
+
+Finish:
+    if (r != EOK)
+        ext4_fs_put_inode_ref(child);
+
+    ext4_fs_put_inode_ref(&parent);
+    return r;
+}
+
+int ext4_frename(const char *path, const char *new_path)
+{
+    int r;
+    struct ext4_mountpoint *mp = ext4_get_mount(path);
+    struct ext4_inode_ref inode_ref;
+
+    if (!mp)
+        return ENOENT;
+
+    EXT4_MP_LOCK(mp);
+
+    r = __ext4_get_inode_ref_remove_hardlink(path, &inode_ref);
+    if (r != EOK)
+        goto Finish;
+
+    r = __ext4_create_hardlink(new_path, &inode_ref);
+    if (r != EOK)
+        r = __ext4_create_hardlink(path, &inode_ref);
+
+    ext4_fs_put_inode_ref(&inode_ref);
+
+Finish:
+    EXT4_MP_UNLOCK(mp);
+    return r;
+
+}
+
+/****************************************************************************/
+
+int ext4_get_sblock(const char *mount_point, struct ext4_sblock **sb)
+{
+    struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
+
+    if (!mp)
+        return ENOENT;
+
+    *sb = &mp->fs.sb;
+    return EOK;
+}
+
 int ext4_cache_write_back(const char *path, bool on)
 {
     struct ext4_mountpoint *mp = ext4_get_mount(path);
@@ -741,15 +981,6 @@
         return r;
     }
 
-    /*Turncate*/
-    ext4_block_cache_write_back(mp->fs.bdev, 1);
-    /*Truncate may be IO heavy. Do it writeback cache mode.*/
-    r = ext4_fs_truncate_inode(&child, 0);
-    ext4_block_cache_write_back(mp->fs.bdev, 0);
-
-    if (r != EOK)
-        goto Finish;
-
     /*Set path*/
     path += name_off;
 
@@ -760,10 +991,25 @@
     if (r != EOK)
         goto Finish;
 
-    r = ext4_fs_free_inode(&child);
-    if (r != EOK)
-        goto Finish;
+    /*Link count is zero, the inode should be freed. */
+    if (!ext4_inode_get_links_count(child.inode)) {
+	printf("ttttt\n");
+        ext4_inode_set_deletion_time(child.inode, 0xFFFFFFFF);
+        /*Turncate*/
+        ext4_block_cache_write_back(mp->fs.bdev, 1);
+        /*Truncate may be IO heavy. Do it writeback cache mode.*/
+        r = ext4_fs_truncate_inode(&child, 0);
+        ext4_block_cache_write_back(mp->fs.bdev, 0);
 
+        if (r != EOK)
+            goto Finish;
+
+        r = ext4_fs_free_inode(&child);
+        if (r != EOK)
+            goto Finish;
+
+    }
+
 Finish:
     ext4_fs_put_inode_ref(&child);
     ext4_fs_put_inode_ref(&parent);
@@ -771,6 +1017,31 @@
     return r;
 }
 
+int ext4_fill_raw_inode(const char *mount_point, uint32_t ino, struct ext4_inode *inode)
+{
+    int r;
+    struct ext4_inode_ref inode_ref;
+    struct ext4_mountpoint *mp = ext4_get_mount(mount_point);
+
+    if (!mp)
+        return ENOENT;
+
+    EXT4_MP_LOCK(mp);
+
+    /*Load parent*/
+    r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
+    if (r != EOK) {
+        EXT4_MP_UNLOCK(mp);
+        return r;
+    }
+
+    memcpy(inode, inode_ref.inode, sizeof(struct ext4_inode));
+
+    ext4_fs_put_inode_ref(&inode_ref);
+    EXT4_MP_UNLOCK(mp);
+    return r;
+}
+
 int ext4_fopen(ext4_file *f, const char *path, const char *flags)
 {
     struct ext4_mountpoint *mp = ext4_get_mount(path);
@@ -787,6 +1058,28 @@
     return r;
 }
 
+int ext4_fopen2(ext4_file *f, const char *path, int flags, bool file_expect)
+{
+    struct ext4_mountpoint *mp = ext4_get_mount(path);
+    int r;
+    int filetype;
+
+    if (!mp)
+        return ENOENT;
+
+    if (file_expect == true)
+        filetype = EXT4_DIRECTORY_FILETYPE_REG_FILE;
+    else
+        filetype = EXT4_DIRECTORY_FILETYPE_DIR;
+
+    EXT4_MP_LOCK(mp);
+    ext4_block_cache_write_back(mp->fs.bdev, 1);
+    r = ext4_generic_open2(f, path, flags, filetype, 0, 0);
+    ext4_block_cache_write_back(mp->fs.bdev, 0);
+    EXT4_MP_UNLOCK(mp);
+    return r;
+}
+
 int ext4_fclose(ext4_file *f)
 {
     ext4_assert(f && f->mp);
@@ -798,6 +1091,59 @@
 
     return EOK;
 }
+
+int ext4_ftruncate(ext4_file *f, uint64_t size)
+{
+    struct ext4_inode_ref ref;
+    int r;
+
+
+    ext4_assert(f && f->mp);
+
+    if (f->flags & O_RDONLY)
+        return EPERM;
+
+    EXT4_MP_LOCK(f->mp);
+
+    r = ext4_fs_get_inode_ref(&f->mp->fs, f->inode, &ref);
+    if (r != EOK) {
+        EXT4_MP_UNLOCK(f->mp);
+        return r;
+    }
+
+    /*Sync file size*/
+    f->fsize = ext4_inode_get_size(&f->mp->fs.sb, ref.inode);
+    if (f->fsize <= size) {
+        r = EOK;
+        goto Finish;
+    }
+
+    /*Start write back cache mode.*/
+    r = ext4_block_cache_write_back(f->mp->fs.bdev, 1);
+    if (r != EOK)
+        goto Finish;
+
+    r = ext4_fs_truncate_inode(&ref, size);
+    if (r != EOK)
+        goto Finish;
+
+    f->fsize = size;
+    if (f->fpos > size)
+        f->fpos = size;
+
+    /*Stop write back cache mode*/
+    ext4_block_cache_write_back(f->mp->fs.bdev, 0);
+
+    if (r != EOK)
+        goto Finish;
+
+Finish:
+    ext4_fs_put_inode_ref(&ref);
+    EXT4_MP_UNLOCK(f->mp);
+    return r;
+
+}
+
 int ext4_fread(ext4_file *f, void *buf, uint32_t size, uint32_t *rcnt)
 {
     uint32_t u;
@@ -1131,10 +1477,154 @@
     return EINVAL;
 }
 
-uint64_t ext4_ftell(ext4_file *f) { return f->fpos; }
+uint64_t ext4_ftell(ext4_file *f)
+{
+    return f->fpos;
+}
 
-uint64_t ext4_fsize(ext4_file *f) { return f->fsize; }
+uint64_t ext4_fsize(ext4_file *f)
+{
+    return f->fsize;
+}
 
+int ext4_fchmod(ext4_file *f, uint32_t mode)
+{
+    int r;
+    uint32_t ino;
+    struct ext4_sblock *sb;
+    struct ext4_inode_ref inode_ref;
+    struct ext4_mountpoint *mp = f->mp;
+
+    if (!mp)
+        return ENOENT;
+
+    EXT4_MP_LOCK(mp);
+
+    ino = f->inode;
+    r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
+    if (r != EOK) {
+        EXT4_MP_UNLOCK(mp);
+        return r;
+    }
+
+    sb = &f->mp->fs.sb;
+    ext4_inode_set_mode(sb, inode_ref.inode, mode);
+    inode_ref.dirty = true;
+
+    ext4_fs_put_inode_ref(&inode_ref);
+    EXT4_MP_UNLOCK(mp);
+    return r;
+}
+
+int ext4_fchown(ext4_file *f, uint32_t uid, uint32_t gid)
+{
+    int r;
+    uint32_t ino;
+    struct ext4_inode_ref inode_ref;
+    struct ext4_mountpoint *mp = f->mp;
+
+    if (!mp)
+        return ENOENT;
+
+    EXT4_MP_LOCK(mp);
+
+    ino = f->inode;
+    r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
+    if (r != EOK) {
+        EXT4_MP_UNLOCK(mp);
+        return r;
+    }
+
+    ext4_inode_set_uid(inode_ref.inode, uid);
+    ext4_inode_set_gid(inode_ref.inode, gid);
+    inode_ref.dirty = true;
+
+    ext4_fs_put_inode_ref(&inode_ref);
+    EXT4_MP_UNLOCK(mp);
+    return r;
+}
+
+int ext4_file_set_atime(ext4_file *f, uint32_t atime)
+{
+    int r;
+    uint32_t ino;
+    struct ext4_inode_ref inode_ref;
+    struct ext4_mountpoint *mp = f->mp;
+
+    if (!mp)
+        return ENOENT;
+
+    EXT4_MP_LOCK(mp);
+
+    ino = f->inode;
+    r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
+    if (r != EOK) {
+        EXT4_MP_UNLOCK(mp);
+        return r;
+    }
+
+    ext4_inode_set_access_time(inode_ref.inode, atime);
+    inode_ref.dirty = true;
+
+    ext4_fs_put_inode_ref(&inode_ref);
+    EXT4_MP_UNLOCK(mp);
+    return r;
+}
+
+int ext4_file_set_mtime(ext4_file *f, uint32_t mtime)
+{
+    int r;
+    uint32_t ino;
+    struct ext4_inode_ref inode_ref;
+    struct ext4_mountpoint *mp = f->mp;
+
+    if (!mp)
+        return ENOENT;
+
+    EXT4_MP_LOCK(mp);
+
+    ino = f->inode;
+    r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
+    if (r != EOK) {
+        EXT4_MP_UNLOCK(mp);
+        return r;
+    }
+
+    ext4_inode_set_modification_time(inode_ref.inode, mtime);
+    inode_ref.dirty = true;
+
+    ext4_fs_put_inode_ref(&inode_ref);
+    EXT4_MP_UNLOCK(mp);
+    return r;
+}
+
+int ext4_file_set_ctime(ext4_file *f, uint32_t ctime)
+{
+    int r;
+    uint32_t ino;
+    struct ext4_inode_ref inode_ref;
+    struct ext4_mountpoint *mp = f->mp;
+
+    if (!mp)
+        return ENOENT;
+
+    EXT4_MP_LOCK(mp);
+
+    ino = f->inode;
+    r = ext4_fs_get_inode_ref(&mp->fs, ino, &inode_ref);
+    if (r != EOK) {
+        EXT4_MP_UNLOCK(mp);
+        return r;
+    }
+
+    ext4_inode_set_change_inode_time(inode_ref.inode, ctime);
+    inode_ref.dirty = true;
+
+    ext4_fs_put_inode_ref(&inode_ref);
+    EXT4_MP_UNLOCK(mp);
+    return r;
+}
+
 /*********************************DIRECTORY OPERATION************************/
 
 int ext4_dir_rm(const char *path)
@@ -1228,17 +1718,20 @@
                     break;
                 }
 
-                /*Directory is empty. Truncate it.*/
-                r = ext4_fs_truncate_inode(&child, 0);
+                /*No children in child directory or file. Just unlink.*/
+                r = ext4_unlink(f.mp, &current, &child,
+                                (char *)it.current->name,
+                                it.current->name_length);
                 if (r != EOK) {
                     ext4_fs_put_inode_ref(&child);
                     break;
                 }
 
-                /*No children in child directory or file. Just unlink.*/
-                r = ext4_unlink(f.mp, &current, &child,
-                                (char *)it.current->name,
-                                it.current->name_length);
+                ext4_inode_set_deletion_time(child.inode, 0xFFFFFFFF);
+                ext4_inode_set_links_count(child.inode, 0);
+                child.dirty = true;
+                /*Turncate*/
+                r = ext4_fs_truncate_inode(&child, 0);
                 if (r != EOK) {
                     ext4_fs_put_inode_ref(&child);
                     break;
@@ -1250,6 +1743,7 @@
                     break;
                 }
 
+
                 r = ext4_fs_put_inode_ref(&child);
                 if (r != EOK)
                     break;
@@ -1274,12 +1768,6 @@
                 if (r != EOK)
                     goto End;
 
-                r = ext4_fs_truncate_inode(&current, 0);
-                if (r != EOK) {
-                    ext4_fs_put_inode_ref(&parent);
-                    goto End;
-                }
-
                 /* In this place all directories should be unlinked.
                  * Last unlink from root of current directory*/
                 r = ext4_unlink(f.mp, &parent, &current, (char *)path, len);
@@ -1288,10 +1776,23 @@
                     goto End;
                 }
 
-                r = ext4_fs_free_inode(&current);
-                if (r != EOK) {
-                    ext4_fs_put_inode_ref(&parent);
-                    goto End;
+                if (ext4_inode_get_links_count(current.inode) == 2) {
+                    ext4_inode_set_deletion_time(current.inode, 0xFFFFFFFF);
+                    ext4_inode_set_links_count(current.inode, 0);
+                    current.dirty = true;
+                    /*Turncate*/
+                    r = ext4_fs_truncate_inode(&current, 0);
+                    if (r != EOK) {
+                        ext4_fs_put_inode_ref(&parent);
+                        goto End;
+                    }
+
+                    r = ext4_fs_free_inode(&current);
+                    if (r != EOK) {
+                        ext4_fs_put_inode_ref(&parent);
+                        goto End;
+                    }
+
                 }
 
                 r = ext4_fs_put_inode_ref(&parent);
--- a/lwext4/ext4.h
+++ b/lwext4/ext4.h
@@ -39,6 +39,7 @@
 #define EXT4_H_
 
 #include "ext4_config.h"
+#include "ext4_types.h"
 #include "ext4_blockdev.h"
 
 #include <stdint.h>
@@ -209,6 +210,12 @@
 int ext4_mount_setup_locks(const char *mount_point,
                            const struct ext4_lock *locks);
 
+/**@brief   Acquire the filesystem superblock pointer of a mp.
+ * @param   mount_point mount path
+ * @param   superblock pointer
+ * @return  standard error code */
+int ext4_get_sblock(const char *mount_point, struct ext4_sblock **sb);
+
 /**@brief   Enable/disable write back cache mode.
  * @warning Default model of cache is write trough. It means that when You do:
  *
@@ -253,6 +260,12 @@
  * @return  standard error code */
 int ext4_fremove(const char *path);
 
+/**@brief Rename file
+ * @param path source
+ * @param new_path destination
+ * @return  standard error code */
+int ext4_frename(const char *path, const char *new_path);
+
 /**@brief   File open function.
  * @param   filename, (has to start from mount point)
  *          /my_partition/my_file
@@ -274,11 +287,31 @@
  * @return  standard error code*/
 int ext4_fopen(ext4_file *f, const char *path, const char *flags);
 
+/**@brief   Alternate file open function.
+ * @param   filename, (has to start from mount point)
+ *          /my_partition/my_file
+ * @param   flags open file flags
+ * @return  standard error code*/
+int ext4_fopen2(ext4_file *f, const char *path, int flags, bool file_expect);
+
 /**@brief   File close function.
  * @param   f file handle
  * @return  standard error code*/
 int ext4_fclose(ext4_file *f);
 
+/**@brief   Fill in the ext4_inode buffer.
+ * @param   mount_point
+ * @param   inode no.
+ * @param   ext4_inode buffer
+ * @return  standard error code*/
+int ext4_fill_raw_inode(const char *mount_point, uint32_t ino, struct ext4_inode *inode);
+
+/**@brief   File truncate function.
+ * @param   f file handle
+ * @param   new file size
+ * @return  standard error code*/
+int ext4_ftruncate(ext4_file *f, uint64_t size);
+
 /**@brief   Read data from file.
  * @param   f file handle
  * @param   buf output buffer
@@ -314,6 +347,12 @@
  * @param   f file handle
  * @return  file size */
 uint64_t ext4_fsize(ext4_file *f);
+
+int ext4_fchmod(ext4_file *f, uint32_t mode);
+int ext4_fchown(ext4_file *f, uint32_t uid, uint32_t gid);
+int ext4_file_set_atime(ext4_file *f, uint32_t atime);
+int ext4_file_set_mtime(ext4_file *f, uint32_t mtime);
+int ext4_file_set_ctime(ext4_file *f, uint32_t ctime);
 
 /*********************************DIRECTORY OPERATION***********************/
 
--- a/lwext4/ext4_fs.c
+++ b/lwext4/ext4_fs.c
@@ -632,7 +632,6 @@
         mode = 0777;
         mode |= EXT4_INODE_MODE_DIRECTORY;
         ext4_inode_set_mode(&fs->sb, inode, mode);
-        ext4_inode_set_links_count(inode, 0);
     } else {
         /*
          * Default file permissions to be compatible with other systems
@@ -642,9 +641,9 @@
         mode = 0666;
         mode |= EXT4_INODE_MODE_FILE;
         ext4_inode_set_mode(&fs->sb, inode, mode);
-        ext4_inode_set_links_count(inode, 1);
     }
 
+    ext4_inode_set_links_count(inode, 0);
     ext4_inode_set_uid(inode, 0);
     ext4_inode_set_gid(inode, 0);
     ext4_inode_set_size(inode, 0);
@@ -1286,11 +1285,6 @@
 void ext4_fs_inode_links_count_inc(struct ext4_inode_ref *inode_ref)
 {
     uint16_t link;
-    if (!ext4_inode_is_type(&inode_ref->fs->sb, inode_ref->inode,
-                            EXT4_INODE_MODE_DIRECTORY)) {
-        ext4_inode_set_links_count(inode_ref->inode, 0);
-        return;
-    }
 
     link = ext4_inode_get_links_count(inode_ref->inode);
     link++;
@@ -1313,13 +1307,13 @@
 
 void ext4_fs_inode_links_count_dec(struct ext4_inode_ref *inode_ref)
 {
+    uint16_t links = ext4_inode_get_links_count(inode_ref->inode);
     if (!ext4_inode_is_type(&inode_ref->fs->sb, inode_ref->inode,
                             EXT4_INODE_MODE_DIRECTORY)) {
-        ext4_inode_set_links_count(inode_ref->inode, 0);
+        if (links > 0)
+            ext4_inode_set_links_count(inode_ref->inode, links - 1);
         return;
     }
-
-    uint16_t links = ext4_inode_get_links_count(inode_ref->inode);
 
     if (links > 2)
         ext4_inode_set_links_count(inode_ref->inode, links - 1);