Closed
Description
since unlinking a directory will reclaim its blocks(by calling put_blocks):
static int simplefs_unlink(struct inode *dir, struct dentry *dentry)
{
struct super_block *sb = dir->i_sb;
struct simplefs_sb_info *sbi = SIMPLEFS_SB(sb);
struct inode *inode = d_inode(dentry);
struct buffer_head *bh = NULL, *bh2 = NULL;
struct simplefs_file_ei_block *file_block = NULL;
#if SIMPLEFS_AT_LEAST(6, 6, 0) && SIMPLEFS_LESS_EQUAL(6, 7, 0)
struct timespec64 cur_time;
#endif
int ei = 0, bi = 0;
int ret = 0;
uint32_t ino = inode->i_ino;
uint32_t bno = 0;
ret = simplefs_remove_from_dir(dir, dentry);
if (ret != 0)
return ret;
if (S_ISLNK(inode->i_mode))
goto clean_inode;
/* Update inode stats */
#if SIMPLEFS_AT_LEAST(6, 7, 0)
simple_inode_init_ts(dir);
#elif SIMPLEFS_AT_LEAST(6, 6, 0)
cur_time = current_time(dir);
dir->i_mtime = dir->i_atime = cur_time;
inode_set_ctime_to_ts(dir, cur_time);
#else
dir->i_mtime = dir->i_atime = dir->i_ctime = current_time(dir);
#endif
if (S_ISDIR(inode->i_mode)) {
drop_nlink(dir);
drop_nlink(inode);
}
mark_inode_dirty(dir);
if (inode->i_nlink > 1) {
inode_dec_link_count(inode);
return ret;
}
/* Cleans up pointed blocks when unlinking a file. If reading the index
* block fails, the inode is cleaned up regardless, resulting in the
* permanent loss of this file's blocks. If scrubbing a data block fails,
* do not terminate the operation (as it is already too late); instead,
* release the block and proceed.
*/
bno = SIMPLEFS_INODE(inode)->ei_block;
bh = sb_bread(sb, bno);
if (!bh)
goto clean_inode;
file_block = (struct simplefs_file_ei_block *) bh->b_data;
if (S_ISDIR(inode->i_mode))
goto scrub;
for (ei = 0; ei < SIMPLEFS_MAX_EXTENTS; ei++) {
char *block;
if (!file_block->extents[ei].ee_start)
break;
put_blocks(sbi, file_block->extents[ei].ee_start,
file_block->extents[ei].ee_len);_
/* Scrub the extent */
for (bi = 0; bi < file_block->extents[ei].ee_len; bi++) {
bh2 = sb_bread(sb, file_block->extents[ei].ee_start + bi);
if (!bh2)
continue;
block = (char *) bh2->b_data;
memset(block, 0, SIMPLEFS_BLOCK_SIZE);
mark_buffer_dirty(bh2);
brelse(bh2);
}
}
scrub:
/* Scrub index block */
memset(file_block, 0, SIMPLEFS_BLOCK_SIZE);
mark_buffer_dirty(bh);
brelse(bh);
clean_inode:
/* Cleanup inode and mark dirty */
inode->i_blocks = 0;
SIMPLEFS_INODE(inode)->ei_block = 0;
inode->i_size = 0;
i_uid_write(inode, 0);
i_gid_write(inode, 0);
#if SIMPLEFS_AT_LEAST(6, 7, 0)
inode_set_mtime(inode, 0, 0);
inode_set_atime(inode, 0, 0);
inode_set_ctime(inode, 0, 0);
#elif SIMPLEFS_AT_LEAST(6, 6, 0)
inode->i_mtime.tv_sec = inode->i_atime.tv_sec = 0;
inode_set_ctime(inode, 0, 0);
#else
inode->i_ctime.tv_sec = inode->i_mtime.tv_sec = inode->i_atime.tv_sec = 0;
#endif
inode_dec_link_count(inode);
/* Free inode and index block from bitmap */
if (!S_ISLNK(inode->i_mode))
put_blocks(sbi, bno, 1);
inode->i_mode = 0;
put_inode(sbi, ino);
return ret;
}
why removing a dentry does not reclaim its blocks(if the block is empty)?
static int simplefs_remove_from_dir(struct inode *dir, struct dentry *dentry)
{
struct super_block *sb = dir->i_sb;
struct inode *inode = d_inode(dentry);
struct buffer_head *bh = NULL, *bh2 = NULL, *bh_prev = NULL;
struct simplefs_file_ei_block *eblock = NULL;
struct simplefs_dir_block *dblock = NULL, *dblock_prev = NULL;
int ei = 0, bi = 0, fi = 0;
int ret = 0, found = false;
/* Read parent directory index */
bh = sb_bread(sb, SIMPLEFS_INODE(dir)->ei_block);
if (!bh)
return -EIO;
eblock = (struct simplefs_file_ei_block *) bh->b_data;
for (ei = 0; ei < SIMPLEFS_MAX_EXTENTS; ei++) {
if (!eblock->extents[ei].ee_start)
break;
for (bi = 0; bi < eblock->extents[ei].ee_len; bi++) {
bh2 = sb_bread(sb, eblock->extents[ei].ee_start + bi);
if (!bh2) {
ret = -EIO;
goto release_bh;
}
dblock = (struct simplefs_dir_block *) bh2->b_data;
if (!dblock->files[0].inode)
break;
if (found) {
memmove(dblock_prev->files + SIMPLEFS_FILES_PER_BLOCK - 1,
dblock->files, sizeof(struct simplefs_file));
brelse(bh_prev);
memmove(dblock->files, dblock->files + 1,
(SIMPLEFS_FILES_PER_BLOCK - 1) *
sizeof(struct simplefs_file));
memset(dblock->files + SIMPLEFS_FILES_PER_BLOCK - 1, 0,
sizeof(struct simplefs_file));
mark_buffer_dirty(bh2);
bh_prev = bh2;
dblock_prev = dblock;
continue;
}
/* Remove file from parent directory */
for (fi = 0; fi < SIMPLEFS_FILES_PER_BLOCK; fi++) {
if (dblock->files[fi].inode == inode->i_ino &&
!strcmp(dblock->files[fi].filename, dentry->d_name.name)) {
found = true;
if (fi != SIMPLEFS_FILES_PER_BLOCK - 1) {
memmove(dblock->files + fi, dblock->files + fi + 1,
(SIMPLEFS_FILES_PER_BLOCK - fi - 1) *
sizeof(struct simplefs_file));
}
memset(dblock->files + SIMPLEFS_FILES_PER_BLOCK - 1, 0,
sizeof(struct simplefs_file));
mark_buffer_dirty(bh2);
bh_prev = bh2;
dblock_prev = dblock;
break;
}
}
if (!found)
brelse(bh2);
}
}
if (found) {
if (bh_prev)
brelse(bh_prev);
eblock->nr_files--;
mark_buffer_dirty(bh);
}
release_bh:
brelse(bh);
return ret;
}
Metadata
Metadata
Assignees
Labels
No labels