上一篇说到ext2文件系统删除文件是逻辑删除,对应文件系统的unlink操作,其实就是解除磁盘目录项与inode关系,同时删除目录项(也是逻辑删除,与前一项合并)。那么,同样删除目录跟删除文件类似,ext2也是做了一次unlink操作。详细的删除目录流程如下:
- ① 检查目录是否为空(除了.和..之外,是否还有其它文件),如果目录不空有文件,返回-ENOTEMPTY,无法删除(相关函数:ext2_empty_dir())
- ② 如果目录为空,调用ext2_unlink()函数解除link
// namei.c
static int ext2_rmdir (struct inode * dir, struct dentry *dentry)
{
struct inode * inode = d_inode(dentry);
int err = -ENOTEMPTY;
if (ext2_empty_dir(inode)) {
err = ext2_unlink(dir, dentry);
if (!err) {
inode->i_size = 0;
inode_dec_link_count(inode);
inode_dec_link_count(dir);
}
}
return err;
}
一、检查目录是否为空
在删除目录之前,要检查目录是否为空,只有空目录才能删除。所谓空目录,就是目录项下除了包含.和..两个目录项之外,不能再有其它任何目录项。所以检查过程,就是遍历目录下的所有目录项,确认是否存在目录项。大体的流程如下:
- 1)计算目录涉及的page页面数
- 2)遍历所有页面
- 3)从页头开始,挨个框选ext2_dir_entry_2结构体,检查整个页面所有entry项
- 4)以下四种情况,返回目录不空:rec_len长度为0、名称非.和..目录项、名称长度大于2、inode与目录的i_ino不一致
// dir.c
/*
* routine to check that the specified directory is empty (for rmdir)
*/
int ext2_empty_dir (struct inode * inode)
{
......
for (i = 0; i < npages; i++) {
char *kaddr;
ext2_dirent * de;
page = ext2_get_page(inode, i, dir_has_error, &page_addr);
......
kaddr = page_addr;
de = (ext2_dirent *)kaddr;
kaddr += ext2_last_byte(inode, i) - EXT2_DIR_REC_LEN(1);
while ((char *)de <= kaddr) {
if (de->rec_len == 0) {
ext2_error(inode->i_sb, __func__,
"zero-length directory entry");
printk("kaddr=%p, de=%p\n", kaddr, de);
goto not_empty;
}
if (de->inode != 0) {
/* check for . and .. */
if (de->name[0] != '.')
goto not_empty;
if (de->name_len > 2)
goto not_empty;
if (de->name_len < 2) {
if (de->inode !=
cpu_to_le32(inode->i_ino))
goto not_empty;
} else if (de->name[1] != '.')
goto not_empty;
}
de = ext2_next_entry(de);
}
ext2_put_page(page, page_addr);
}
return 1;
not_empty:
ext2_put_page(page, page_addr);
return 0;
}
二、解除目录项与Inode的link(略)
unlink操作在前一篇已经介绍,在这里不过多赘述,想进一步研究的,点击下方链接。
删除文件:08 ext2文件系统IO流程:删除文件unlink
三、递减链接计数器
这一步共三个操作:
- 1)将目录的inode中i_size置0
- 2)递减目录的inode中的i_nlink域(硬链接),同时置inode脏
- 3)递减父目录的inode中i_nlink域(),同时置inode脏
inode->i_size = 0;
inode_dec_link_count(inode);
inode_dec_link_count(dir);
static inline void inode_dec_link_count(struct inode *inode)
{
drop_nlink(inode);
mark_inode_dirty(inode);
}
参考资料:VFS
内核版本:5.16.7