Skip to content

Commit

Permalink
f2fs: do not expose unwritten blocks to user by DIO
Browse files Browse the repository at this point in the history
DIO preallocates physical blocks before writing data, but if an error occurrs
or power-cut happens, we can see block contents from the disk. This patch tries
to fix it by 1) turning to buffered writes for DIO into holes, 2) truncating
unwritten blocks from error or power-cut.

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
  • Loading branch information
Jaegeuk Kim committed Dec 4, 2021
1 parent b31bf0f commit d4dd19e
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 10 deletions.
5 changes: 4 additions & 1 deletion fs/f2fs/data.c
Original file line number Diff line number Diff line change
Expand Up @@ -1543,8 +1543,11 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map,
flag != F2FS_GET_BLOCK_DIO);
err = __allocate_data_block(&dn,
map->m_seg_type);
if (!err)
if (!err) {
if (flag == F2FS_GET_BLOCK_PRE_DIO)
file_need_truncate(inode);
set_inode_flag(inode, FI_APPEND_WRITE);
}
}
if (err)
goto sync_out;
Expand Down
5 changes: 5 additions & 0 deletions fs/f2fs/f2fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,7 @@ enum {
#define FADVISE_KEEP_SIZE_BIT 0x10
#define FADVISE_HOT_BIT 0x20
#define FADVISE_VERITY_BIT 0x40
#define FADVISE_TRUNC_BIT 0x80

#define FADVISE_MODIFIABLE_BITS (FADVISE_COLD_BIT | FADVISE_HOT_BIT)

Expand Down Expand Up @@ -681,6 +682,10 @@ enum {
#define file_is_verity(inode) is_file(inode, FADVISE_VERITY_BIT)
#define file_set_verity(inode) set_file(inode, FADVISE_VERITY_BIT)

#define file_should_truncate(inode) is_file(inode, FADVISE_TRUNC_BIT)
#define file_need_truncate(inode) set_file(inode, FADVISE_TRUNC_BIT)
#define file_dont_truncate(inode) clear_file(inode, FADVISE_TRUNC_BIT)

#define DEF_DIR_LEVEL 0

enum {
Expand Down
27 changes: 18 additions & 9 deletions fs/f2fs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -1687,6 +1687,7 @@ static int expand_inode_data(struct inode *inode, loff_t offset,

map.m_seg_type = CURSEG_COLD_DATA_PINNED;
err = f2fs_map_blocks(inode, &map, 1, F2FS_GET_BLOCK_PRE_DIO);
file_dont_truncate(inode);

up_write(&sbi->pin_sem);

Expand Down Expand Up @@ -4257,6 +4258,13 @@ static int f2fs_preallocate_blocks(struct kiocb *iocb, struct iov_iter *iter)
/* If it will be an out-of-place direct write, don't bother. */
if (dio && f2fs_lfs_mode(sbi))
return 0;
/*
* Don't preallocate holes aligned to DIO_SKIP_HOLES which turns into
* buffered IO, if DIO meets any holes.
*/
if (dio && i_size_read(inode) &&
(F2FS_BYTES_TO_BLK(pos) < F2FS_BLK_ALIGN(i_size_read(inode))))
return 0;

/* No-wait I/O can't allocate blocks. */
if (iocb->ki_flags & IOCB_NOWAIT)
Expand Down Expand Up @@ -4292,8 +4300,8 @@ static int f2fs_preallocate_blocks(struct kiocb *iocb, struct iov_iter *iter)
}

ret = f2fs_map_blocks(inode, &map, 1, flag);
/* -ENOSPC is only a fatal error if no blocks could be allocated. */
if (ret < 0 && !(ret == -ENOSPC && map.m_len > 0))
/* -ENOSPC|-EDQUOT are fine to report the number of allocated blocks. */
if (ret < 0 && !((ret == -ENOSPC || ret == -EDQUOT) && map.m_len > 0))
return ret;
if (ret == 0)
set_inode_flag(inode, FI_PREALLOCATED_ALL);
Expand Down Expand Up @@ -4359,20 +4367,21 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
/* Possibly preallocate the blocks for the write. */
target_size = iocb->ki_pos + iov_iter_count(from);
preallocated = f2fs_preallocate_blocks(iocb, from);
if (preallocated < 0) {
if (preallocated < 0)
ret = preallocated;
goto out_unlock;
}

ret = __generic_file_write_iter(iocb, from);
else
ret = __generic_file_write_iter(iocb, from);

/* Don't leave any preallocated blocks around past i_size. */
if (preallocated > 0 && i_size_read(inode) < target_size) {
if (preallocated && i_size_read(inode) < target_size) {
down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
filemap_invalidate_lock(inode->i_mapping);
f2fs_truncate(inode);
if (!f2fs_truncate(inode))
file_dont_truncate(inode);
filemap_invalidate_unlock(inode->i_mapping);
up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
} else {
file_dont_truncate(inode);
}

clear_inode_flag(inode, FI_PREALLOCATED_ALL);
Expand Down
8 changes: 8 additions & 0 deletions fs/f2fs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,14 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino)
goto bad_inode;
}
f2fs_set_inode_flags(inode);

if (file_should_truncate(inode)) {
ret = f2fs_truncate(inode);
if (ret)
goto bad_inode;
file_dont_truncate(inode);
}

unlock_new_inode(inode);
trace_f2fs_iget(inode);
return inode;
Expand Down

0 comments on commit d4dd19e

Please sign in to comment.