前面几篇博客讲了创建文件、创建目录、查找文件,接下来说一下ext2文件系统是如何删除文件,ext2文件系统删除文件,并没有在物理设备上擦除文件,只是做了一个unlink操作,所谓的逻辑删除。unlink的具体实现,就是解除目录项与inode关系,这样目录项就不会关联到inode,就无法查找到文件,达到了删除的目的。unlink的具体的流程如下:
- ① 根据文件名和父目录信息,到磁盘上查找目录项(ext2_find_entry),记录对应的页面page和页地址page_addr
- ② 解除目录项与inode连接,然后删除目录项(逻辑删除,其实是与前一个目录项合并)
- ③ 修改inode->i_ctime,递减inode的硬链接数i->nlink,同时标记脏inode
static int ext2_unlink(struct inode * dir, struct dentry *dentry)
{
struct inode * inode = d_inode(dentry);
......
de = ext2_find_entry(dir, &dentry->d_name, &page, &page_addr);
if (IS_ERR(de)) {
err = PTR_ERR(de);
goto out;
}
err = ext2_delete_entry(de, page, page_addr);
ext2_put_page(page, page_addr);
if (err)
goto out;
inode->i_ctime = dir->i_ctime;
inode_dec_link_count(inode);
err = 0;
out:
return err;
}
一、查询文件的目录项(磁盘)
与上一篇查询文件的查找目录项逻辑一致(ext2_find_entry()),就是根据循环父目录相关的所有页面,找到目录项位置,在此不做过多赘述,详细参考如下链接。
上一篇:07 ext2文件系统IO流程:文件查找lookup
二、“删除”磁盘目录项
这里的删除加了引号,其实是逻辑删除,最主要就两个动作:一是将inode设为0,也就是不指向任何inode,二是将目录项与前一条合并(修改前一条的rec_len)。
经过这两个动作后,要删除文件的目录项,就不指向inode,在用户端就无法展示,同时合并两个目录项之后,目录项数据仍然是连续的,可以顺序读取。
// dir.c
/*
* ext2_delete_entry deletes a directory entry by merging it with the
* previous entry. Page is up-to-date.
*/
int ext2_delete_entry (struct ext2_dir_entry_2 *dir, struct page *page,
char *kaddr)
{
struct inode *inode = page->mapping->host;
unsigned from = ((char*)dir - kaddr) & ~(ext2_chunk_size(inode)-1);
unsigned to = ((char *)dir - kaddr) +
ext2_rec_len_from_disk(dir->rec_len);
loff_t pos;
ext2_dirent * pde = NULL; // 记录前一条entry
ext2_dirent * de = (ext2_dirent *) (kaddr + from);
int err;
while ((char*)de < (char*)dir) {
if (de->rec_len == 0) {
ext2_error(inode->i_sb, __func__,
"zero-length directory entry");
err = -EIO;
goto out;
}
pde = de;
de = ext2_next_entry(de);
}
if (pde)
from = (char *)pde - kaddr;
pos = page_offset(page) + from;
lock_page(page);
err = ext2_prepare_chunk(page, pos, to - from); // 准备要写入的块
BUG_ON(err);
if (pde)
pde->rec_len = ext2_rec_len_to_disk(to - from); // 修改长度
dir->inode = 0; // inode置0
err = ext2_commit_chunk(page, pos, to - from); // 提交页面修改
inode->i_ctime = inode->i_mtime = current_time(inode);
EXT2_I(inode)->i_flags &= ~EXT2_BTREE_FL;
mark_inode_dirty(inode);
out:
return err;
}
三、递减链接数
第二步解除目录项与inode关系,但是一个文件可能被多个目录项使用,不能直接释放inode资源。通过递减inode中的硬链接数,当硬链接数为0时,VFS会调用super_block的操作表完成inode释放工作。
inode->i_ctime = dir->i_ctime;
inode_dec_link_count(inode);
static inline void inode_dec_link_count(struct inode *inode)
{
drop_nlink(inode);
mark_inode_dirty(inode);
}
官方文档:VFS
《08 ext2文件系统IO流程:删除文件unlink》有一个想法