static int do_ext2fs_rename()

in fs/extfs/extfs.cpp [370:478]


static int do_ext2fs_rename(ext2_filsys fs, const char *from, const char *to) {
    errcode_t ret = 0;
    ext2_ino_t from_ino, to_ino, to_dir_ino, from_dir_ino;
    struct ext2_inode inode;
    struct update_dotdot ud;

    DEFER(LOG_DEBUG("rename ", VALUE(from), VALUE(to), VALUE(from_ino), VALUE(to_ino), VALUE(ret)));

    from_ino = string_to_inode(fs, from, 0, true);
    if (from_ino == 0) {
        return -(ret = ENOENT);
    }
    to_ino = string_to_inode(fs, to, 0);
    if (to_ino == 0 && errno != ENOENT) {
        return -(ret = errno);
    }

    /* Already the same file? */
    if (to_ino != 0 && to_ino == from_ino)
        return 0;

    /* Find parent dir of the source and check write access */
    from_dir_ino = get_parent_dir_ino(fs, from);
    if (from_dir_ino == 0) {
        return -(ret = ENOTDIR);
    }

    /* Find parent dir of the destination and check write access */
    to_dir_ino = get_parent_dir_ino(fs, to);
    if (to_dir_ino == 0) {
        return -(ret = ENOTDIR);
    }
    char *filename = get_filename(to);
    if (filename == nullptr) {
        return -(ret = EISDIR);
    }

    /* If the target exists, unlink it first */
    if (to_ino != 0) {
        ret = ext2fs_read_inode(fs, to_ino, &inode);
        if (ret) return parse_extfs_error(fs, to_ino, ret);

        LOG_DEBUG("unlinking ` ino=`", LINUX_S_ISDIR(inode.i_mode) ? "dir" : "file", to_ino);
        if (LINUX_S_ISDIR(inode.i_mode))
            ret = do_ext2fs_rmdir(fs, to);
        else
            ret = do_ext2fs_unlink(fs, to);
        if (ret) return ret;
    }

    /* Get ready to do the move */
    ret = ext2fs_read_inode(fs, from_ino, &inode);
    if (ret) return parse_extfs_error(fs, from_ino, ret);

    /* Link in the new file */
    LOG_DEBUG("linking ino=`/path=` to dir=`", from_ino, filename, to_dir_ino);
    ret = ext2fs_link(fs, to_dir_ino, filename, from_ino, ext2_file_type(inode.i_mode));
    if (ret == EXT2_ET_DIR_NO_SPACE) {
        ret = ext2fs_expand_dir(fs, to_dir_ino);
        if (ret) return parse_extfs_error(fs, to_dir_ino, ret);

        ret = ext2fs_link(fs, to_dir_ino, filename, from_ino, ext2_file_type(inode.i_mode));
    }
    if (ret) return parse_extfs_error(fs, to_dir_ino, ret);

    /* Update '..' pointer if dir */
    ret = ext2fs_read_inode(fs, from_ino, &inode);
    if (ret) return parse_extfs_error(fs, from_ino, ret);

    if (LINUX_S_ISDIR(inode.i_mode)) {
        ud.new_dotdot = to_dir_ino;
        LOG_DEBUG("updating .. entry for dir=`", to_dir_ino);
        ret = ext2fs_dir_iterate2(fs, from_ino, 0, nullptr, update_dotdot_helper, &ud);
        if (ret) return parse_extfs_error(fs, from_ino, ret);

        /* Decrease from_dir_ino's links_count */
        LOG_DEBUG("moving linkcount from dir=` to dir=`", from_dir_ino, to_dir_ino);
        ret = ext2fs_read_inode(fs, from_dir_ino, &inode);
        if (ret) return parse_extfs_error(fs, from_dir_ino, ret);
        inode.i_links_count--;
        ret = ext2fs_write_inode(fs, from_dir_ino, &inode);
        if (ret) return parse_extfs_error(fs, from_dir_ino, ret);

        /* Increase to_dir_ino's links_count */
        ret = ext2fs_read_inode(fs, to_dir_ino, &inode);
        if (ret) return parse_extfs_error(fs, to_dir_ino, ret);
        inode.i_links_count++;
        ret = ext2fs_write_inode(fs, to_dir_ino, &inode);
        if (ret) return parse_extfs_error(fs, to_dir_ino, ret);
    }

    /* Update timestamps */
    // update_ctime
    ret = update_xtime(fs, from_ino, nullptr, EXT_CTIME);
    if (ret) return ret;
    // update_mtime
    ret = update_xtime(fs, to_dir_ino, nullptr, EXT_CTIME | EXT_MTIME);
    if (ret) return ret;

    /* Remove the old file */
    ret = unlink_file_by_name(fs, from);
    if (ret) return ret;

    /* Flush the whole mess out */
    ret = ext2fs_flush2(fs, 0);
    if (ret) return parse_extfs_error(fs, 0, ret);

    return 0;
}