试验环境
代码语言:javascript
复制package main
import (
"flag"
"fmt" "os")
var (
oldName = flag.String("o","src_name","default source file name")
newName = flag.String("n","dst_name","default dst file name")
)
func main() {
flag.Parse()
if err := os.Rename(*oldName,*newName);err != nil {
fmt.Println("rename failed:",err)
return
}
fmt.Println("rename succ")
}
代码语言:javascript
复制$ cp ~/1.go a_file
$ cp ~/1.go b_file
// a_file inode= 167996098
// b_file inode= 167996099
$ ls -i
167996098 a_file 167996099 b_file 268630153 rename_file
$ ./rename_file -o a_file -n b_file
rename succ
// rename完成后,b_file的inode就是a_file的inode
$ ls -i
167996098 b_file 268630153 rename_file
结论
- rename的posix语义是原子语义,要么成功要么失败;
- rename的新文件从试验的过程中可以看出,old_name对应的文件的inode就是new_name对应文件的inode,new_name的文件在操作完成后被删删除。
- rename过程是记录rename过程的日志,然后删除new_name对应inode和old_name对用的dentry.接着更新new_name对应的dentry中的inode为old_name的inode
分析
代码语言:javascript
复制// 这里分析的是ext4本地文件系统,
int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry,
struct inode **delegated_inode, unsigned int flags)
{
struct inode *source = old_dentry->d_inode;
struct inode *target = new_dentry->d_inode;
if (source == target)
return 0;
// 如果target的dentry dcache不存在,则创建
if (!target) {
error = may_create(new_dir, new_dentry);
} else {
// 否则就删除dcache dentry
new_is_dir = d_is_dir(new_dentry);
if (!(flags & RENAME_EXCHANGE))
error = may_delete(new_dir, new_dentry, is_dir);
else
error = may_delete(new_dir, new_dentry, new_is_dir);
}
// 最后调用具体ext4文件系统的rename函数
error = old_dir->i_op->rename(old_dir, old_dentry,
new_dir, new_dentry, flags);
}
// 保留大体的流程的ext4_rename的函数
static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry,
unsigned int flags)
{
handle_t *handle = NULL;
struct ext4_renament old = {
.dir = old_dir,
.dentry = old_dentry,
.inode = d_inode(old_dentry),
};
struct ext4_renament new = {
.dir = new_dir,
.dentry = new_dentry,
.inode = d_inode(new_dentry),
};
// 查找dcache的entry,并释放旧文件的entry
old.bh = ext4_find_entry(old.dir, &old.dentry->d_name, &old.de, NULL);
if (IS_ERR(old.bh))
return PTR_ERR(old.bh);
// 开始记录rename的日志
handle = ext4_journal_start(old.dir, EXT4_HT_DIR, credits);
// 如果new_name的文件不存在,则old_name的inode设置到新的dentry中
if (!new.bh) {
retval = ext4_add_entry(handle, new.dentry, old.inode);
if (retval)
goto end_rename;
} else {
// new_name文件存在,设置new_name中的inode为就文件的inode
retval = ext4_setent(handle, &new,
old.inode->i_ino, old_file_type);
}
// 释放rename过程中临时构造的ext4_renament
ext4_rename_delete(handle, &old, force_reread);
// 日志记录结束
ext4_journal_stop(handle);
}
// dir inode的操作函数
const struct inode_operations ext4_dir_inode_operations = {
.create = ext4_create,
.lookup = ext4_lookup,
.link = ext4_link,
.unlink = ext4_unlink,
.symlink = ext4_symlink,
.mkdir = ext4_mkdir,
.rmdir = ext4_rmdir,
.mknod = ext4_mknod,
.tmpfile = ext4_tmpfile,
.rename = ext4_rename2,
.setattr = ext4_setattr,
.getattr = ext4_getattr,
.listxattr = ext4_listxattr,
.get_acl = ext4_get_acl,
.set_acl = ext4_set_acl,
.fiemap = ext4_fiemap,
};