09 ext2文件系统IO流程:删除目录rmdir

上一篇说到ext2文件系统删除文件是逻辑删除,对应文件系统的unlink操作,其实就是解除磁盘目录项与inode关系,同时删除目录项(也是逻辑删除,与前一项合并)。那么,同样删除目录跟删除文件类似,ext2也是做了一次unlink操作。详细的删除目录流程如下:

  • ① 检查目录是否为空(除了.和..之外,是否还有其它文件),如果目录不空有文件,返回-ENOTEMPTY,无法删除(相关函数:ext2_empty_dir())
  • ② 如果目录为空,调用ext2_unlink()函数解除link
ext2删除目录流程
ext2删除目录流程
// 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

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注