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;
}