F2FS源码分析-6.6 [其他重要数据结构以及函数] F2FS的重命名过程-f2fs_rename函数

F2FS源码分析系列文章
主目录
一、文件系统布局以及元数据结构
二、文件数据的存储以及读写
三、文件与目录的创建以及删除(未完成)
四、垃圾回收机制
五、数据恢复机制
六、重要数据结构或者函数的分析
  1. f2fs_summary的作用
  2. f2fs_journal的作用
  3. f2fs_map_block的作用
  4. get_dnode_of_data的作用
  5. get_node_page的作用(未完成)

F2FS的rename流程

rename流程介绍

  1. sys_rename函数
  2. do_renameat2函数
  3. vfs_rename函数
  4. f2fs_rename函数

sys_rename函数

sys_rename函数是一个系统调用,是rename函数进入内核层的第一个函数:

SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newname)
{
    // AT_FDCWD表示以相对路径的方法找oldname和newname这个文件,flags=0
	return do_renameat2(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
}

do_renameat2函数

do_renameat2函数比较长,考虑多个输入flag的作用,这里只考虑sys_rename函数rename一个文件的情形,即flag=0,并以此精简函数。

static int do_renameat2(int olddfd, const char __user *oldname, int newdfd,
			const char __user *newname, unsigned int flags)
{
	struct dentry *old_dentry, *new_dentry;
	struct dentry *trap;
	struct path old_path, new_path;
	struct qstr old_last, new_last;
	int old_type, new_type;
	struct inode *delegated_inode = NULL;
	struct filename *from;
	struct filename *to;
	unsigned int lookup_flags = 0, target_flags = LOOKUP_RENAME_TARGET;
	bool should_retry = false;
	int error;

retry:
    // 接下来两个函数最重要的作用是根据oldname和newname找到父目录的dentry结构
    // 这两个dentry结构保存在old_path和new_path中(注意是父目录的dentry)
	from = filename_parentat(olddfd, getname(oldname), lookup_flags,
				&old_path, &old_last, &old_type);

	to = filename_parentat(newdfd, getname(newname), lookup_flags,
				&new_path, &new_last, &new_type);
    
retry_deleg:
    // 这个函数会触发一个全局的rename的互斥锁,然后锁两个父目录inode结构
	trap = lock_rename(new_path.dentry, old_path.dentry);
	// 根据old path的父目录找到需要被rename的文件的dentry
	old_dentry = __lookup_hash(&old_last, old_path.dentry, lookup_flags);
	// 根据new path的父目录找到或创建新的dentry
	new_dentry = __lookup_hash(&new_last, new_path.dentry, lookup_flags | target_flags);
	// 调用vfs_rename函数进行重命名
    // 传入的是新旧两个目录的inode,以及需要重命名的两个dentry, flags = 0
	error = vfs_rename(old_path.dentry->d_inode, old_dentry,
			   new_path.dentry->d_inode, new_dentry,
			   &delegated_inode, flags);

	dput(new_dentry);

	dput(old_dentry);
    // 解锁全局rename互斥锁,释放两个inode锁
	unlock_rename(new_path.dentry, old_path.dentry);

	path_put(&new_path);
	putname(to);

	path_put(&old_path);
	putname(from);
exit:
	return error;
}

vfs_rename函数

vfs_rename函数也会做简化,简化的情形是将文件A重命名到文件B (B可能已经存在,或者不存在),flags=0。

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)
{
	int error;
	bool is_dir = d_is_dir(old_dentry);
	struct inode *source = old_dentry->d_inode; // 旧文件inode
	struct inode *target = new_dentry->d_inode; // 新文件inode
	bool new_is_dir = false;
	unsigned max_links = new_dir->i_sb->s_max_links;
	struct name_snapshot old_name;

	dget(new_dentry); // 对新文件的引用计数+1
	if (target)
		inode_lock(target); // 如果新文件已经存在,则上锁


	error = old_dir->i_op->rename(old_dir, old_dentry,
				       new_dir, new_dentry, flags);


out:
	if (target)
		inode_unlock(target); // 如果新文件已经存在,则解锁
	dput(new_dentry); // 对新文件的引用计数-1
	return error;
}

f2fs_rename函数

f2fs_rename函数也会做简化,简化的情形是将文件A重命名到文件B (B可能已经存在,或者不存在),flags=0。

static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
			struct inode *new_dir, struct dentry *new_dentry,
			unsigned int flags)
{
	struct f2fs_sb_info *sbi = F2FS_I_SB(old_dir);
	struct inode *old_inode = d_inode(old_dentry);
	struct inode *new_inode = d_inode(new_dentry);
	struct inode *whiteout = NULL;
	struct page *old_dir_page;
	struct page *old_page, *new_page = NULL;
	struct f2fs_dir_entry *old_dir_entry = NULL;
	struct f2fs_dir_entry *old_entry;
	struct f2fs_dir_entry *new_entry;
	bool is_old_inline = f2fs_has_inline_dentry(old_dir);
	int err;
	
	// 输入显然是
    // 旧的父目录old_dir,旧的文件old_dentry
    // 新的父目录new_dir,新的文件new_dentry
    
    // 根据旧文件的名字找到对应的f2fs_dir_entry,old_page保存的是磁盘上的dir_entry数据
	old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page);

	if (new_inode) { // 如果新文件已经存在

        // 根据新文件的名字找到对应的f2fs_dir_entry,new_page保存的是磁盘上的数据
		new_entry = f2fs_find_entry(new_dir, &new_dentry->d_name,
						&new_page);

        // F2FS获取一个全局读信号量
		f2fs_lock_op(sbi);

        // 在管理orphan inode的全局结构中,将orphan inode的数目+1。
		err = f2fs_acquire_orphan_inode(sbi);

        // 这里进行新旧inode的link的变化:
        // 将new_dentry所属的inode指向old_inode
        // 因为rename的时候新inode是已经存在了,因此rename的操作就是将
        // 新路径原来的inode无效掉,然后替换为旧路径的inode
		f2fs_set_link(new_dir, new_entry, new_page, old_inode);

		new_inode->i_ctime = current_time(new_inode);
        
        
		down_write(&F2FS_I(new_inode)->i_sem); // 拿写信号量
		// 减少新inode一个引用计数,因为被rename了
		f2fs_i_links_write(new_inode, false);
		up_write(&F2FS_I(new_inode)->i_sem); // 释放写信号量

        // 如果引用计数下降到0,则添加到orphan inode中,在checkpoint管理
		if (!new_inode->i_nlink)
			f2fs_add_orphan_inode(new_inode);
		else
			f2fs_release_orphan_inode(sbi); // 否则管理结构将orphan inode的数目-1。
	} else {
        
        // 这个情况是新路径的Inode不存在

		// F2FS获取一个全局读信号量
		f2fs_lock_op(sbi);

        // 由于新inode是不存在的,因此直接将旧inode添加到新的f2fs_dir_entry中
		err = f2fs_add_link(new_dentry, old_inode);

	}

    
	down_write(&F2FS_I(old_inode)->i_sem);
	if (!old_dir_entry || whiteout)
		file_lost_pino(old_inode);  // 这个操作要保留着用于数据恢复
	else
		F2FS_I(old_inode)->i_pino = new_dir->i_ino;
	up_write(&F2FS_I(old_inode)->i_sem);

	old_inode->i_ctime = current_time(old_inode);
	f2fs_mark_inode_dirty_sync(old_inode, false);

    // 新的数据已经加入到新的f2fs_dir_entry,因此旧entry就去去除掉
	f2fs_delete_entry(old_entry, old_page, old_dir, NULL);

	// F2FS释放全局读信号量
	f2fs_unlock_op(sbi);

	f2fs_update_time(sbi, REQ_TIME);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 您好!要重命名文件或文件夹,可以使用Python的`os`模块中的`rename()`函数。 这是一个示例代码,将文件夹中的所有文件名从"old_name"更改为"new_name": ``` python import os folder_path = "path/to/folder" old_name = "old_name" new_name = "new_name" for filename in os.listdir(folder_path): if filename.startswith(old_name): new_filename = filename.replace(old_name, new_name) os.rename(os.path.join(folder_path, filename), os.path.join(folder_path, new_filename)) ``` 请注意,这将更改文件或文件夹的实际名称,因此请小心操作。建议在运行代码之前备份所有数据。 希望这可以帮助您! ### 回答2: Python的rename函数是用于重命名文件或目录的函数,它可以将一个已存在的文件或目录重命名为一个新名称。重命名过程是在文件系统级别操作,因此需要相应的文件系统权限。 该函数定义为: ```python os.rename(src, dst) ``` 其中,src和dst分别表示源文件或目录的路径和目标文件或目录的路径。如果dst已经存在,rename函数会抛出异常。 使用rename函数可以方便地实现对文件或目录的重命名操作,例如: ```python import os # 将test.txt重命名为test1.txt os.rename('test.txt', 'test1.txt') # 将test目录重命名为test1 os.rename('test', 'test1') ``` 需要注意的是,对于目录的重命名操作,只能重命名该目录而不能重命名该目录下的文件或目录。如果需要修改该目录下的文件或目录的名称,需要使用os.walk()或其他实现递归访问的方式进行。 同时,在进行重命名操作时,应注意文件或目录的完整性和操作的安全性,避免误操作或损坏数据。 ### 回答3: Python中的rename函数是用于重命名文件或目录的一个内置函数,它能够在不改变文件或目录内的内容和数据的前提下,修改它们的名称。 使用重命名函数可以对文件或目录进行多种操作,如改变文件或目录名、移动文件或目录、复制文件或目录、合并文件或目录等。 Python中的rename函数原型如下: os.rename(旧文件名, 新文件名) 其中旧文件名和新文件名是字符串类型的参数,表示需要被重命名的文件或目录的原名称和新名称。 使用rename函数进行文件或目录重命名操作时,需要确保: 1.被重命名的文件或目录存在 2.新的文件或目录名称合法,不会与现有文件或目录重名 3.有足够的权限执行重命名操作,以免因权限问题导致操作失败 例如,下面的代码演示了如何在Python中使用rename函数对文件进行重命名: import os # 指定需要重命名的文件名 old_name = "test.txt" # 指定新的文件名 new_name = "newtest.txt" try: # 重命名文件 os.rename(old_name, new_name) except: print("重命名文件失败") 上述代码中,我们使用Python内置的os库提供的rename函数重命名文件,将原始文件名从“test.txt”更改为“newtest.txt”。 注意,此代码是一个简单示例,仅适用于重命名文件,而不是文件夹。如果你需要重命名文件夹或目录,请使用rename函数并考虑一些新的参数,例如目标文件夹的路径。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值