| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #include <linux/f2fs_fs.h> |
| |
| #include "f2fs.h" |
| #include "xattr.h" |
| |
| #define F2FS_VERIFY_VER (1) |
| |
| static inline loff_t f2fs_verity_metadata_pos(const struct inode *inode) |
| { |
| <------>return round_up(inode->i_size, 65536); |
| } |
| |
| |
| |
| |
| |
| static int pagecache_read(struct inode *inode, void *buf, size_t count, |
| <------><------><------> loff_t pos) |
| { |
| <------>while (count) { |
| <------><------>size_t n = min_t(size_t, count, |
| <------><------><------><------> PAGE_SIZE - offset_in_page(pos)); |
| <------><------>struct page *page; |
| <------><------>void *addr; |
| |
| <------><------>page = read_mapping_page(inode->i_mapping, pos >> PAGE_SHIFT, |
| <------><------><------><------><------> NULL); |
| <------><------>if (IS_ERR(page)) |
| <------><------><------>return PTR_ERR(page); |
| |
| <------><------>addr = kmap_atomic(page); |
| <------><------>memcpy(buf, addr + offset_in_page(pos), n); |
| <------><------>kunmap_atomic(addr); |
| |
| <------><------>put_page(page); |
| |
| <------><------>buf += n; |
| <------><------>pos += n; |
| <------><------>count -= n; |
| <------>} |
| <------>return 0; |
| } |
| |
| |
| |
| |
| |
| static int pagecache_write(struct inode *inode, const void *buf, size_t count, |
| <------><------><------> loff_t pos) |
| { |
| <------>if (pos + count > inode->i_sb->s_maxbytes) |
| <------><------>return -EFBIG; |
| |
| <------>while (count) { |
| <------><------>size_t n = min_t(size_t, count, |
| <------><------><------><------> PAGE_SIZE - offset_in_page(pos)); |
| <------><------>struct page *page; |
| <------><------>void *fsdata; |
| <------><------>void *addr; |
| <------><------>int res; |
| |
| <------><------>res = pagecache_write_begin(NULL, inode->i_mapping, pos, n, 0, |
| <------><------><------><------><------> &page, &fsdata); |
| <------><------>if (res) |
| <------><------><------>return res; |
| |
| <------><------>addr = kmap_atomic(page); |
| <------><------>memcpy(addr + offset_in_page(pos), buf, n); |
| <------><------>kunmap_atomic(addr); |
| |
| <------><------>res = pagecache_write_end(NULL, inode->i_mapping, pos, n, n, |
| <------><------><------><------><------> page, fsdata); |
| <------><------>if (res < 0) |
| <------><------><------>return res; |
| <------><------>if (res != n) |
| <------><------><------>return -EIO; |
| |
| <------><------>buf += n; |
| <------><------>pos += n; |
| <------><------>count -= n; |
| <------>} |
| <------>return 0; |
| } |
| |
| |
| |
| |
| |
| |
| |
| struct fsverity_descriptor_location { |
| <------>__le32 version; |
| <------>__le32 size; |
| <------>__le64 pos; |
| }; |
| |
| static int f2fs_begin_enable_verity(struct file *filp) |
| { |
| <------>struct inode *inode = file_inode(filp); |
| <------>int err; |
| |
| <------>if (f2fs_verity_in_progress(inode)) |
| <------><------>return -EBUSY; |
| |
| <------>if (f2fs_is_atomic_file(inode) || f2fs_is_volatile_file(inode)) |
| <------><------>return -EOPNOTSUPP; |
| |
| <------> |
| <------> * Since the file was opened readonly, we have to initialize the quotas |
| <------> * here and not rely on ->open() doing it. This must be done before |
| <------> * evicting the inline data. |
| <------> */ |
| <------>err = dquot_initialize(inode); |
| <------>if (err) |
| <------><------>return err; |
| |
| <------>err = f2fs_convert_inline_inode(inode); |
| <------>if (err) |
| <------><------>return err; |
| |
| <------>set_inode_flag(inode, FI_VERITY_IN_PROGRESS); |
| <------>return 0; |
| } |
| |
| static int f2fs_end_enable_verity(struct file *filp, const void *desc, |
| <------><------><------><------> size_t desc_size, u64 merkle_tree_size) |
| { |
| <------>struct inode *inode = file_inode(filp); |
| <------>struct f2fs_sb_info *sbi = F2FS_I_SB(inode); |
| <------>u64 desc_pos = f2fs_verity_metadata_pos(inode) + merkle_tree_size; |
| <------>struct fsverity_descriptor_location dloc = { |
| <------><------>.version = cpu_to_le32(F2FS_VERIFY_VER), |
| <------><------>.size = cpu_to_le32(desc_size), |
| <------><------>.pos = cpu_to_le64(desc_pos), |
| <------>}; |
| <------>int err = 0, err2 = 0; |
| |
| <------> |
| <------> * If an error already occurred (which fs/verity/ signals by passing |
| <------> * desc == NULL), then only clean-up is needed. |
| <------> */ |
| <------>if (desc == NULL) |
| <------><------>goto cleanup; |
| |
| <------> |
| <------>err = pagecache_write(inode, desc, desc_size, desc_pos); |
| <------>if (err) |
| <------><------>goto cleanup; |
| |
| <------> |
| <------> * Write all pages (both data and verity metadata). Note that this must |
| <------> * happen before clearing FI_VERITY_IN_PROGRESS; otherwise pages beyond |
| <------> * i_size won't be written properly. For crash consistency, this also |
| <------> * must happen before the verity inode flag gets persisted. |
| <------> */ |
| <------>err = filemap_write_and_wait(inode->i_mapping); |
| <------>if (err) |
| <------><------>goto cleanup; |
| |
| <------> |
| <------>err = f2fs_setxattr(inode, F2FS_XATTR_INDEX_VERITY, |
| <------><------><------> F2FS_XATTR_NAME_VERITY, &dloc, sizeof(dloc), |
| <------><------><------> NULL, XATTR_CREATE); |
| <------>if (err) |
| <------><------>goto cleanup; |
| |
| <------> |
| <------>file_set_verity(inode); |
| <------>f2fs_set_inode_flags(inode); |
| <------>f2fs_mark_inode_dirty_sync(inode, true); |
| |
| <------>clear_inode_flag(inode, FI_VERITY_IN_PROGRESS); |
| <------>return 0; |
| |
| cleanup: |
| <------> |
| <------> * Verity failed to be enabled, so clean up by truncating any verity |
| <------> * metadata that was written beyond i_size (both from cache and from |
| <------> * disk) and clearing FI_VERITY_IN_PROGRESS. |
| <------> * |
| <------> * Taking i_gc_rwsem[WRITE] is needed to stop f2fs garbage collection |
| <------> * from re-instantiating cached pages we are truncating (since unlike |
| <------> * normal file accesses, garbage collection isn't limited by i_size). |
| <------> */ |
| <------>f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); |
| <------>truncate_inode_pages(inode->i_mapping, inode->i_size); |
| <------>err2 = f2fs_truncate(inode); |
| <------>if (err2) { |
| <------><------>f2fs_err(sbi, "Truncating verity metadata failed (errno=%d)", |
| <------><------><------> err2); |
| <------><------>set_sbi_flag(sbi, SBI_NEED_FSCK); |
| <------>} |
| <------>f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); |
| <------>clear_inode_flag(inode, FI_VERITY_IN_PROGRESS); |
| <------>return err ?: err2; |
| } |
| |
| static int f2fs_get_verity_descriptor(struct inode *inode, void *buf, |
| <------><------><------><------> size_t buf_size) |
| { |
| <------>struct fsverity_descriptor_location dloc; |
| <------>int res; |
| <------>u32 size; |
| <------>u64 pos; |
| |
| <------> |
| <------>res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_VERITY, |
| <------><------><------> F2FS_XATTR_NAME_VERITY, &dloc, sizeof(dloc), NULL); |
| <------>if (res < 0 && res != -ERANGE) |
| <------><------>return res; |
| <------>if (res != sizeof(dloc) || dloc.version != cpu_to_le32(F2FS_VERIFY_VER)) { |
| <------><------>f2fs_warn(F2FS_I_SB(inode), "unknown verity xattr format"); |
| <------><------>return -EINVAL; |
| <------>} |
| <------>size = le32_to_cpu(dloc.size); |
| <------>pos = le64_to_cpu(dloc.pos); |
| |
| <------> |
| <------>if (pos + size < pos || pos + size > inode->i_sb->s_maxbytes || |
| <------> pos < f2fs_verity_metadata_pos(inode) || size > INT_MAX) { |
| <------><------>f2fs_warn(F2FS_I_SB(inode), "invalid verity xattr"); |
| <------><------>return -EFSCORRUPTED; |
| <------>} |
| <------>if (buf_size) { |
| <------><------>if (size > buf_size) |
| <------><------><------>return -ERANGE; |
| <------><------>res = pagecache_read(inode, buf, size, pos); |
| <------><------>if (res) |
| <------><------><------>return res; |
| <------>} |
| <------>return size; |
| } |
| |
| static struct page *f2fs_read_merkle_tree_page(struct inode *inode, |
| <------><------><------><------><------> pgoff_t index, |
| <------><------><------><------><------> unsigned long num_ra_pages) |
| { |
| <------>DEFINE_READAHEAD(ractl, NULL, inode->i_mapping, index); |
| <------>struct page *page; |
| |
| <------>index += f2fs_verity_metadata_pos(inode) >> PAGE_SHIFT; |
| |
| <------>page = find_get_page_flags(inode->i_mapping, index, FGP_ACCESSED); |
| <------>if (!page || !PageUptodate(page)) { |
| <------><------>if (page) |
| <------><------><------>put_page(page); |
| <------><------>else if (num_ra_pages > 1) |
| <------><------><------>page_cache_ra_unbounded(&ractl, num_ra_pages, 0); |
| <------><------>page = read_mapping_page(inode->i_mapping, index, NULL); |
| <------>} |
| <------>return page; |
| } |
| |
| static int f2fs_write_merkle_tree_block(struct inode *inode, const void *buf, |
| <------><------><------><------><------>u64 index, int log_blocksize) |
| { |
| <------>loff_t pos = f2fs_verity_metadata_pos(inode) + (index << log_blocksize); |
| |
| <------>return pagecache_write(inode, buf, 1 << log_blocksize, pos); |
| } |
| |
| const struct fsverity_operations f2fs_verityops = { |
| <------>.begin_enable_verity = f2fs_begin_enable_verity, |
| <------>.end_enable_verity = f2fs_end_enable_verity, |
| <------>.get_verity_descriptor = f2fs_get_verity_descriptor, |
| <------>.read_merkle_tree_page = f2fs_read_merkle_tree_page, |
| <------>.write_merkle_tree_block = f2fs_write_merkle_tree_block, |
| }; |
| |