diff --git a/lwext4/ext4.c b/lwext4/ext4.c index 107f55c..2bb783f 100644 --- a/lwext4/ext4.c +++ b/lwext4/ext4.c @@ -180,7 +180,7 @@ static int ext4_has_children(bool *has_children, struct ext4_inode_ref *enode) static int ext4_link(struct ext4_mountpoint *mp, struct ext4_inode_ref *parent, struct ext4_inode_ref *child, const char *name, - uint32_t name_len) + uint32_t name_len, bool rename) { /* Check maximum name length */ if (name_len > EXT4_DIRECTORY_FILENAME_LEN) @@ -196,7 +196,7 @@ static int ext4_link(struct ext4_mountpoint *mp, struct ext4_inode_ref *parent, */ if (ext4_inode_is_type(&mp->fs.sb, child->inode, EXT4_INODE_MODE_DIRECTORY) && - ext4_inode_get_links_count(child->inode) == 0) { + !rename) { rc = ext4_dir_add_entry(child, ".", strlen("."), child); if (rc != EOK) { ext4_dir_remove_entry(parent, name, strlen(name)); @@ -231,6 +231,10 @@ static int ext4_link(struct ext4_mountpoint *mp, struct ext4_inode_ref *parent, child->dirty = true; parent->dirty = true; } else { + /* + * In case we want to rename a directory, + * we reset the original '..' pointer. + */ if (ext4_inode_is_type(&mp->fs.sb, child->inode, EXT4_INODE_MODE_DIRECTORY)) { int has_flag_index = @@ -263,7 +267,8 @@ static int ext4_link(struct ext4_mountpoint *mp, struct ext4_inode_ref *parent, ext4_fs_inode_links_count_inc(parent); parent->dirty = true; - } else { + } + if (!rename) { ext4_fs_inode_links_count_inc(child); child->dirty = true; } @@ -632,6 +637,9 @@ static int ext4_generic_open2(ext4_file *f, const char *path, int flags, r = ext4_dir_find_entry(&result, &ref, path, len); if (r != EOK) { + /*Destroy last result*/ + ext4_dir_destroy_result(&ref, &result); + if (r != ENOENT) break; @@ -647,11 +655,9 @@ static int ext4_generic_open2(ext4_file *f, const char *path, int flags, if (r != EOK) break; - /*Destroy last result*/ - ext4_dir_destroy_result(&ref, &result); /*Link with root dir.*/ - r = ext4_link(mp, &ref, &child_ref, path, len); + r = ext4_link(mp, &ref, &child_ref, path, len, false); if (r != EOK) { /*Fail. Free new inode.*/ ext4_fs_free_inode(&child_ref); @@ -758,7 +764,8 @@ static int ext4_generic_open(ext4_file *f, const char *path, const char *flags, } static int __ext4_create_hardlink(const char *path, - struct ext4_inode_ref *child_ref) + struct ext4_inode_ref *child_ref, + bool rename) { bool is_goal = false; uint8_t inode_type = EXT4_DIRENTRY_DIR; @@ -799,14 +806,19 @@ static int __ext4_create_hardlink(const char *path, r = ext4_dir_find_entry(&result, &ref, path, len); if (r != EOK) { + /*Destroy last result*/ + ext4_dir_destroy_result(&ref, &result); + if (r != ENOENT || !is_goal) break; + /*Link with root dir.*/ + r = ext4_link(mp, &ref, child_ref, path, len, rename); + break; + } else if (r == EOK && is_goal) { /*Destroy last result*/ ext4_dir_destroy_result(&ref, &result); - - /*Link with root dir.*/ - r = ext4_link(mp, &ref, child_ref, path, len); + r = EEXIST; break; } @@ -850,10 +862,10 @@ static int __ext4_create_hardlink(const char *path, return r; } -static int __ext4_remove_hardlink(const char *path, - uint32_t name_off, - struct ext4_inode_ref *parent_ref, - struct ext4_inode_ref *child_ref) +static int ext4_remove_orig_reference(const char *path, + uint32_t name_off, + struct ext4_inode_ref *parent_ref, + struct ext4_inode_ref *child_ref) { bool is_goal; int r; @@ -868,11 +880,16 @@ static int __ext4_remove_hardlink(const char *path, len = ext4_path_check(path, &is_goal); - /*Unlink from parent*/ - r = ext4_unlink(mp, parent_ref, child_ref, path, len); + /* Remove entry from parent directory */ + r = ext4_dir_remove_entry(parent_ref, path, len); if (r != EOK) goto Finish; + if (ext4_inode_is_type(&mp->fs.sb, child_ref->inode, + EXT4_INODE_MODE_DIRECTORY)) { + ext4_fs_inode_links_count_dec(parent_ref); + parent_ref->dirty = true; + } Finish: return r; } @@ -913,7 +930,14 @@ int ext4_flink(const char *path, const char *hardlink_path) child_loaded = true; - r = __ext4_create_hardlink(hardlink_path, &child_ref); + /* Creating hardlink for directory is not allowed. */ + if (ext4_inode_is_type(&mp->fs.sb, child_ref.inode, + EXT4_INODE_MODE_DIRECTORY)) { + r = EINVAL; + goto Finish; + } + + r = __ext4_create_hardlink(hardlink_path, &child_ref, false); Finish: if (child_loaded) @@ -962,11 +986,12 @@ int ext4_frename(const char *path, const char *new_path) child_loaded = true; - r = __ext4_create_hardlink(new_path, &child_ref); + r = __ext4_create_hardlink(new_path, &child_ref, true); if (r != EOK) goto Finish; - r = __ext4_remove_hardlink(path, name_off, &parent_ref, &child_ref); + r = ext4_remove_orig_reference(path, name_off, + &parent_ref, &child_ref); if (r != EOK) goto Finish;