探究ext4文件系统中的rename

2022-08-17 12:49:39 浏览数 (1)

试验环境
  • rename测试代码
代码语言: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")  
  
}
  • rename测试过程
代码语言: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,
};

0 人点赞