diff options
-rw-r--r-- | fs/ext4/Makefile | 1 | ||||
-rw-r--r-- | fs/ext4/ext4.h | 21 | ||||
-rw-r--r-- | fs/ext4/file.c | 4 | ||||
-rw-r--r-- | fs/ext4/inode.c | 53 | ||||
-rw-r--r-- | fs/ext4/ioctl.c | 13 | ||||
-rw-r--r-- | fs/ext4/super.c | 9 | ||||
-rw-r--r-- | fs/ext4/sysfs.c | 6 | ||||
-rw-r--r-- | fs/ext4/verity.c | 367 |
8 files changed, 457 insertions, 17 deletions
diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile index 8fdfcd3c3e04..b17ddc229ac5 100644 --- a/fs/ext4/Makefile +++ b/fs/ext4/Makefile | |||
@@ -13,3 +13,4 @@ ext4-y := balloc.o bitmap.o block_validity.o dir.o ext4_jbd2.o extents.o \ | |||
13 | 13 | ||
14 | ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o | 14 | ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o |
15 | ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o | 15 | ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o |
16 | ext4-$(CONFIG_FS_VERITY) += verity.o | ||
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index bf660aa7a9e0..736972f46ea6 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -41,6 +41,7 @@ | |||
41 | #endif | 41 | #endif |
42 | 42 | ||
43 | #include <linux/fscrypt.h> | 43 | #include <linux/fscrypt.h> |
44 | #include <linux/fsverity.h> | ||
44 | 45 | ||
45 | #include <linux/compiler.h> | 46 | #include <linux/compiler.h> |
46 | 47 | ||
@@ -395,6 +396,7 @@ struct flex_groups { | |||
395 | #define EXT4_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/ | 396 | #define EXT4_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/ |
396 | #define EXT4_HUGE_FILE_FL 0x00040000 /* Set to each huge file */ | 397 | #define EXT4_HUGE_FILE_FL 0x00040000 /* Set to each huge file */ |
397 | #define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */ | 398 | #define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */ |
399 | #define EXT4_VERITY_FL 0x00100000 /* Verity protected inode */ | ||
398 | #define EXT4_EA_INODE_FL 0x00200000 /* Inode used for large EA */ | 400 | #define EXT4_EA_INODE_FL 0x00200000 /* Inode used for large EA */ |
399 | #define EXT4_EOFBLOCKS_FL 0x00400000 /* Blocks allocated beyond EOF */ | 401 | #define EXT4_EOFBLOCKS_FL 0x00400000 /* Blocks allocated beyond EOF */ |
400 | #define EXT4_INLINE_DATA_FL 0x10000000 /* Inode has inline data. */ | 402 | #define EXT4_INLINE_DATA_FL 0x10000000 /* Inode has inline data. */ |
@@ -402,7 +404,7 @@ struct flex_groups { | |||
402 | #define EXT4_CASEFOLD_FL 0x40000000 /* Casefolded file */ | 404 | #define EXT4_CASEFOLD_FL 0x40000000 /* Casefolded file */ |
403 | #define EXT4_RESERVED_FL 0x80000000 /* reserved for ext4 lib */ | 405 | #define EXT4_RESERVED_FL 0x80000000 /* reserved for ext4 lib */ |
404 | 406 | ||
405 | #define EXT4_FL_USER_VISIBLE 0x704BDFFF /* User visible flags */ | 407 | #define EXT4_FL_USER_VISIBLE 0x705BDFFF /* User visible flags */ |
406 | #define EXT4_FL_USER_MODIFIABLE 0x604BC0FF /* User modifiable flags */ | 408 | #define EXT4_FL_USER_MODIFIABLE 0x604BC0FF /* User modifiable flags */ |
407 | 409 | ||
408 | /* Flags we can manipulate with through EXT4_IOC_FSSETXATTR */ | 410 | /* Flags we can manipulate with through EXT4_IOC_FSSETXATTR */ |
@@ -467,6 +469,7 @@ enum { | |||
467 | EXT4_INODE_TOPDIR = 17, /* Top of directory hierarchies*/ | 469 | EXT4_INODE_TOPDIR = 17, /* Top of directory hierarchies*/ |
468 | EXT4_INODE_HUGE_FILE = 18, /* Set to each huge file */ | 470 | EXT4_INODE_HUGE_FILE = 18, /* Set to each huge file */ |
469 | EXT4_INODE_EXTENTS = 19, /* Inode uses extents */ | 471 | EXT4_INODE_EXTENTS = 19, /* Inode uses extents */ |
472 | EXT4_INODE_VERITY = 20, /* Verity protected inode */ | ||
470 | EXT4_INODE_EA_INODE = 21, /* Inode used for large EA */ | 473 | EXT4_INODE_EA_INODE = 21, /* Inode used for large EA */ |
471 | EXT4_INODE_EOFBLOCKS = 22, /* Blocks allocated beyond EOF */ | 474 | EXT4_INODE_EOFBLOCKS = 22, /* Blocks allocated beyond EOF */ |
472 | EXT4_INODE_INLINE_DATA = 28, /* Data in inode. */ | 475 | EXT4_INODE_INLINE_DATA = 28, /* Data in inode. */ |
@@ -512,6 +515,7 @@ static inline void ext4_check_flag_values(void) | |||
512 | CHECK_FLAG_VALUE(TOPDIR); | 515 | CHECK_FLAG_VALUE(TOPDIR); |
513 | CHECK_FLAG_VALUE(HUGE_FILE); | 516 | CHECK_FLAG_VALUE(HUGE_FILE); |
514 | CHECK_FLAG_VALUE(EXTENTS); | 517 | CHECK_FLAG_VALUE(EXTENTS); |
518 | CHECK_FLAG_VALUE(VERITY); | ||
515 | CHECK_FLAG_VALUE(EA_INODE); | 519 | CHECK_FLAG_VALUE(EA_INODE); |
516 | CHECK_FLAG_VALUE(EOFBLOCKS); | 520 | CHECK_FLAG_VALUE(EOFBLOCKS); |
517 | CHECK_FLAG_VALUE(INLINE_DATA); | 521 | CHECK_FLAG_VALUE(INLINE_DATA); |
@@ -1560,6 +1564,7 @@ enum { | |||
1560 | EXT4_STATE_MAY_INLINE_DATA, /* may have in-inode data */ | 1564 | EXT4_STATE_MAY_INLINE_DATA, /* may have in-inode data */ |
1561 | EXT4_STATE_EXT_PRECACHED, /* extents have been precached */ | 1565 | EXT4_STATE_EXT_PRECACHED, /* extents have been precached */ |
1562 | EXT4_STATE_LUSTRE_EA_INODE, /* Lustre-style ea_inode */ | 1566 | EXT4_STATE_LUSTRE_EA_INODE, /* Lustre-style ea_inode */ |
1567 | EXT4_STATE_VERITY_IN_PROGRESS, /* building fs-verity Merkle tree */ | ||
1563 | }; | 1568 | }; |
1564 | 1569 | ||
1565 | #define EXT4_INODE_BIT_FNS(name, field, offset) \ | 1570 | #define EXT4_INODE_BIT_FNS(name, field, offset) \ |
@@ -1610,6 +1615,12 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei) | |||
1610 | #define EXT4_SB(sb) (sb) | 1615 | #define EXT4_SB(sb) (sb) |
1611 | #endif | 1616 | #endif |
1612 | 1617 | ||
1618 | static inline bool ext4_verity_in_progress(struct inode *inode) | ||
1619 | { | ||
1620 | return IS_ENABLED(CONFIG_FS_VERITY) && | ||
1621 | ext4_test_inode_state(inode, EXT4_STATE_VERITY_IN_PROGRESS); | ||
1622 | } | ||
1623 | |||
1613 | #define NEXT_ORPHAN(inode) EXT4_I(inode)->i_dtime | 1624 | #define NEXT_ORPHAN(inode) EXT4_I(inode)->i_dtime |
1614 | 1625 | ||
1615 | /* | 1626 | /* |
@@ -1662,6 +1673,7 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei) | |||
1662 | #define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM 0x0400 | 1673 | #define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM 0x0400 |
1663 | #define EXT4_FEATURE_RO_COMPAT_READONLY 0x1000 | 1674 | #define EXT4_FEATURE_RO_COMPAT_READONLY 0x1000 |
1664 | #define EXT4_FEATURE_RO_COMPAT_PROJECT 0x2000 | 1675 | #define EXT4_FEATURE_RO_COMPAT_PROJECT 0x2000 |
1676 | #define EXT4_FEATURE_RO_COMPAT_VERITY 0x8000 | ||
1665 | 1677 | ||
1666 | #define EXT4_FEATURE_INCOMPAT_COMPRESSION 0x0001 | 1678 | #define EXT4_FEATURE_INCOMPAT_COMPRESSION 0x0001 |
1667 | #define EXT4_FEATURE_INCOMPAT_FILETYPE 0x0002 | 1679 | #define EXT4_FEATURE_INCOMPAT_FILETYPE 0x0002 |
@@ -1756,6 +1768,7 @@ EXT4_FEATURE_RO_COMPAT_FUNCS(bigalloc, BIGALLOC) | |||
1756 | EXT4_FEATURE_RO_COMPAT_FUNCS(metadata_csum, METADATA_CSUM) | 1768 | EXT4_FEATURE_RO_COMPAT_FUNCS(metadata_csum, METADATA_CSUM) |
1757 | EXT4_FEATURE_RO_COMPAT_FUNCS(readonly, READONLY) | 1769 | EXT4_FEATURE_RO_COMPAT_FUNCS(readonly, READONLY) |
1758 | EXT4_FEATURE_RO_COMPAT_FUNCS(project, PROJECT) | 1770 | EXT4_FEATURE_RO_COMPAT_FUNCS(project, PROJECT) |
1771 | EXT4_FEATURE_RO_COMPAT_FUNCS(verity, VERITY) | ||
1759 | 1772 | ||
1760 | EXT4_FEATURE_INCOMPAT_FUNCS(compression, COMPRESSION) | 1773 | EXT4_FEATURE_INCOMPAT_FUNCS(compression, COMPRESSION) |
1761 | EXT4_FEATURE_INCOMPAT_FUNCS(filetype, FILETYPE) | 1774 | EXT4_FEATURE_INCOMPAT_FUNCS(filetype, FILETYPE) |
@@ -1813,7 +1826,8 @@ EXT4_FEATURE_INCOMPAT_FUNCS(casefold, CASEFOLD) | |||
1813 | EXT4_FEATURE_RO_COMPAT_BIGALLOC |\ | 1826 | EXT4_FEATURE_RO_COMPAT_BIGALLOC |\ |
1814 | EXT4_FEATURE_RO_COMPAT_METADATA_CSUM|\ | 1827 | EXT4_FEATURE_RO_COMPAT_METADATA_CSUM|\ |
1815 | EXT4_FEATURE_RO_COMPAT_QUOTA |\ | 1828 | EXT4_FEATURE_RO_COMPAT_QUOTA |\ |
1816 | EXT4_FEATURE_RO_COMPAT_PROJECT) | 1829 | EXT4_FEATURE_RO_COMPAT_PROJECT |\ |
1830 | EXT4_FEATURE_RO_COMPAT_VERITY) | ||
1817 | 1831 | ||
1818 | #define EXTN_FEATURE_FUNCS(ver) \ | 1832 | #define EXTN_FEATURE_FUNCS(ver) \ |
1819 | static inline bool ext4_has_unknown_ext##ver##_compat_features(struct super_block *sb) \ | 1833 | static inline bool ext4_has_unknown_ext##ver##_compat_features(struct super_block *sb) \ |
@@ -3283,6 +3297,9 @@ extern int ext4_bio_write_page(struct ext4_io_submit *io, | |||
3283 | /* mmp.c */ | 3297 | /* mmp.c */ |
3284 | extern int ext4_multi_mount_protect(struct super_block *, ext4_fsblk_t); | 3298 | extern int ext4_multi_mount_protect(struct super_block *, ext4_fsblk_t); |
3285 | 3299 | ||
3300 | /* verity.c */ | ||
3301 | extern const struct fsverity_operations ext4_verityops; | ||
3302 | |||
3286 | /* | 3303 | /* |
3287 | * Add new method to test whether block and inode bitmaps are properly | 3304 | * Add new method to test whether block and inode bitmaps are properly |
3288 | * initialized. With uninit_bg reading the block from disk is not enough | 3305 | * initialized. With uninit_bg reading the block from disk is not enough |
diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 70b0438dbc94..b8a20bb9a145 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c | |||
@@ -457,6 +457,10 @@ static int ext4_file_open(struct inode * inode, struct file * filp) | |||
457 | if (ret) | 457 | if (ret) |
458 | return ret; | 458 | return ret; |
459 | 459 | ||
460 | ret = fsverity_file_open(inode, filp); | ||
461 | if (ret) | ||
462 | return ret; | ||
463 | |||
460 | /* | 464 | /* |
461 | * Set up the jbd2_inode if we are opening the inode for | 465 | * Set up the jbd2_inode if we are opening the inode for |
462 | * writing and the journal is present | 466 | * writing and the journal is present |
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 420fe3deed39..6de3d4ba28f3 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -1340,6 +1340,9 @@ retry_journal: | |||
1340 | } | 1340 | } |
1341 | 1341 | ||
1342 | if (ret) { | 1342 | if (ret) { |
1343 | bool extended = (pos + len > inode->i_size) && | ||
1344 | !ext4_verity_in_progress(inode); | ||
1345 | |||
1343 | unlock_page(page); | 1346 | unlock_page(page); |
1344 | /* | 1347 | /* |
1345 | * __block_write_begin may have instantiated a few blocks | 1348 | * __block_write_begin may have instantiated a few blocks |
@@ -1349,11 +1352,11 @@ retry_journal: | |||
1349 | * Add inode to orphan list in case we crash before | 1352 | * Add inode to orphan list in case we crash before |
1350 | * truncate finishes | 1353 | * truncate finishes |
1351 | */ | 1354 | */ |
1352 | if (pos + len > inode->i_size && ext4_can_truncate(inode)) | 1355 | if (extended && ext4_can_truncate(inode)) |
1353 | ext4_orphan_add(handle, inode); | 1356 | ext4_orphan_add(handle, inode); |
1354 | 1357 | ||
1355 | ext4_journal_stop(handle); | 1358 | ext4_journal_stop(handle); |
1356 | if (pos + len > inode->i_size) { | 1359 | if (extended) { |
1357 | ext4_truncate_failed_write(inode); | 1360 | ext4_truncate_failed_write(inode); |
1358 | /* | 1361 | /* |
1359 | * If truncate failed early the inode might | 1362 | * If truncate failed early the inode might |
@@ -1406,6 +1409,7 @@ static int ext4_write_end(struct file *file, | |||
1406 | int ret = 0, ret2; | 1409 | int ret = 0, ret2; |
1407 | int i_size_changed = 0; | 1410 | int i_size_changed = 0; |
1408 | int inline_data = ext4_has_inline_data(inode); | 1411 | int inline_data = ext4_has_inline_data(inode); |
1412 | bool verity = ext4_verity_in_progress(inode); | ||
1409 | 1413 | ||
1410 | trace_ext4_write_end(inode, pos, len, copied); | 1414 | trace_ext4_write_end(inode, pos, len, copied); |
1411 | if (inline_data) { | 1415 | if (inline_data) { |
@@ -1423,12 +1427,16 @@ static int ext4_write_end(struct file *file, | |||
1423 | /* | 1427 | /* |
1424 | * it's important to update i_size while still holding page lock: | 1428 | * it's important to update i_size while still holding page lock: |
1425 | * page writeout could otherwise come in and zero beyond i_size. | 1429 | * page writeout could otherwise come in and zero beyond i_size. |
1430 | * | ||
1431 | * If FS_IOC_ENABLE_VERITY is running on this inode, then Merkle tree | ||
1432 | * blocks are being written past EOF, so skip the i_size update. | ||
1426 | */ | 1433 | */ |
1427 | i_size_changed = ext4_update_inode_size(inode, pos + copied); | 1434 | if (!verity) |
1435 | i_size_changed = ext4_update_inode_size(inode, pos + copied); | ||
1428 | unlock_page(page); | 1436 | unlock_page(page); |
1429 | put_page(page); | 1437 | put_page(page); |
1430 | 1438 | ||
1431 | if (old_size < pos) | 1439 | if (old_size < pos && !verity) |
1432 | pagecache_isize_extended(inode, old_size, pos); | 1440 | pagecache_isize_extended(inode, old_size, pos); |
1433 | /* | 1441 | /* |
1434 | * Don't mark the inode dirty under page lock. First, it unnecessarily | 1442 | * Don't mark the inode dirty under page lock. First, it unnecessarily |
@@ -1439,7 +1447,7 @@ static int ext4_write_end(struct file *file, | |||
1439 | if (i_size_changed || inline_data) | 1447 | if (i_size_changed || inline_data) |
1440 | ext4_mark_inode_dirty(handle, inode); | 1448 | ext4_mark_inode_dirty(handle, inode); |
1441 | 1449 | ||
1442 | if (pos + len > inode->i_size && ext4_can_truncate(inode)) | 1450 | if (pos + len > inode->i_size && !verity && ext4_can_truncate(inode)) |
1443 | /* if we have allocated more blocks and copied | 1451 | /* if we have allocated more blocks and copied |
1444 | * less. We will have blocks allocated outside | 1452 | * less. We will have blocks allocated outside |
1445 | * inode->i_size. So truncate them | 1453 | * inode->i_size. So truncate them |
@@ -1450,7 +1458,7 @@ errout: | |||
1450 | if (!ret) | 1458 | if (!ret) |
1451 | ret = ret2; | 1459 | ret = ret2; |
1452 | 1460 | ||
1453 | if (pos + len > inode->i_size) { | 1461 | if (pos + len > inode->i_size && !verity) { |
1454 | ext4_truncate_failed_write(inode); | 1462 | ext4_truncate_failed_write(inode); |
1455 | /* | 1463 | /* |
1456 | * If truncate failed early the inode might still be | 1464 | * If truncate failed early the inode might still be |
@@ -1511,6 +1519,7 @@ static int ext4_journalled_write_end(struct file *file, | |||
1511 | unsigned from, to; | 1519 | unsigned from, to; |
1512 | int size_changed = 0; | 1520 | int size_changed = 0; |
1513 | int inline_data = ext4_has_inline_data(inode); | 1521 | int inline_data = ext4_has_inline_data(inode); |
1522 | bool verity = ext4_verity_in_progress(inode); | ||
1514 | 1523 | ||
1515 | trace_ext4_journalled_write_end(inode, pos, len, copied); | 1524 | trace_ext4_journalled_write_end(inode, pos, len, copied); |
1516 | from = pos & (PAGE_SIZE - 1); | 1525 | from = pos & (PAGE_SIZE - 1); |
@@ -1540,13 +1549,14 @@ static int ext4_journalled_write_end(struct file *file, | |||
1540 | if (!partial) | 1549 | if (!partial) |
1541 | SetPageUptodate(page); | 1550 | SetPageUptodate(page); |
1542 | } | 1551 | } |
1543 | size_changed = ext4_update_inode_size(inode, pos + copied); | 1552 | if (!verity) |
1553 | size_changed = ext4_update_inode_size(inode, pos + copied); | ||
1544 | ext4_set_inode_state(inode, EXT4_STATE_JDATA); | 1554 | ext4_set_inode_state(inode, EXT4_STATE_JDATA); |
1545 | EXT4_I(inode)->i_datasync_tid = handle->h_transaction->t_tid; | 1555 | EXT4_I(inode)->i_datasync_tid = handle->h_transaction->t_tid; |
1546 | unlock_page(page); | 1556 | unlock_page(page); |
1547 | put_page(page); | 1557 | put_page(page); |
1548 | 1558 | ||
1549 | if (old_size < pos) | 1559 | if (old_size < pos && !verity) |
1550 | pagecache_isize_extended(inode, old_size, pos); | 1560 | pagecache_isize_extended(inode, old_size, pos); |
1551 | 1561 | ||
1552 | if (size_changed || inline_data) { | 1562 | if (size_changed || inline_data) { |
@@ -1555,7 +1565,7 @@ static int ext4_journalled_write_end(struct file *file, | |||
1555 | ret = ret2; | 1565 | ret = ret2; |
1556 | } | 1566 | } |
1557 | 1567 | ||
1558 | if (pos + len > inode->i_size && ext4_can_truncate(inode)) | 1568 | if (pos + len > inode->i_size && !verity && ext4_can_truncate(inode)) |
1559 | /* if we have allocated more blocks and copied | 1569 | /* if we have allocated more blocks and copied |
1560 | * less. We will have blocks allocated outside | 1570 | * less. We will have blocks allocated outside |
1561 | * inode->i_size. So truncate them | 1571 | * inode->i_size. So truncate them |
@@ -1566,7 +1576,7 @@ errout: | |||
1566 | ret2 = ext4_journal_stop(handle); | 1576 | ret2 = ext4_journal_stop(handle); |
1567 | if (!ret) | 1577 | if (!ret) |
1568 | ret = ret2; | 1578 | ret = ret2; |
1569 | if (pos + len > inode->i_size) { | 1579 | if (pos + len > inode->i_size && !verity) { |
1570 | ext4_truncate_failed_write(inode); | 1580 | ext4_truncate_failed_write(inode); |
1571 | /* | 1581 | /* |
1572 | * If truncate failed early the inode might still be | 1582 | * If truncate failed early the inode might still be |
@@ -2162,7 +2172,8 @@ static int ext4_writepage(struct page *page, | |||
2162 | 2172 | ||
2163 | trace_ext4_writepage(page); | 2173 | trace_ext4_writepage(page); |
2164 | size = i_size_read(inode); | 2174 | size = i_size_read(inode); |
2165 | if (page->index == size >> PAGE_SHIFT) | 2175 | if (page->index == size >> PAGE_SHIFT && |
2176 | !ext4_verity_in_progress(inode)) | ||
2166 | len = size & ~PAGE_MASK; | 2177 | len = size & ~PAGE_MASK; |
2167 | else | 2178 | else |
2168 | len = PAGE_SIZE; | 2179 | len = PAGE_SIZE; |
@@ -2246,7 +2257,8 @@ static int mpage_submit_page(struct mpage_da_data *mpd, struct page *page) | |||
2246 | * after page tables are updated. | 2257 | * after page tables are updated. |
2247 | */ | 2258 | */ |
2248 | size = i_size_read(mpd->inode); | 2259 | size = i_size_read(mpd->inode); |
2249 | if (page->index == size >> PAGE_SHIFT) | 2260 | if (page->index == size >> PAGE_SHIFT && |
2261 | !ext4_verity_in_progress(mpd->inode)) | ||
2250 | len = size & ~PAGE_MASK; | 2262 | len = size & ~PAGE_MASK; |
2251 | else | 2263 | else |
2252 | len = PAGE_SIZE; | 2264 | len = PAGE_SIZE; |
@@ -2345,6 +2357,9 @@ static int mpage_process_page_bufs(struct mpage_da_data *mpd, | |||
2345 | ext4_lblk_t blocks = (i_size_read(inode) + i_blocksize(inode) - 1) | 2357 | ext4_lblk_t blocks = (i_size_read(inode) + i_blocksize(inode) - 1) |
2346 | >> inode->i_blkbits; | 2358 | >> inode->i_blkbits; |
2347 | 2359 | ||
2360 | if (ext4_verity_in_progress(inode)) | ||
2361 | blocks = EXT_MAX_BLOCKS; | ||
2362 | |||
2348 | do { | 2363 | do { |
2349 | BUG_ON(buffer_locked(bh)); | 2364 | BUG_ON(buffer_locked(bh)); |
2350 | 2365 | ||
@@ -3061,8 +3076,8 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping, | |||
3061 | 3076 | ||
3062 | index = pos >> PAGE_SHIFT; | 3077 | index = pos >> PAGE_SHIFT; |
3063 | 3078 | ||
3064 | if (ext4_nonda_switch(inode->i_sb) || | 3079 | if (ext4_nonda_switch(inode->i_sb) || S_ISLNK(inode->i_mode) || |
3065 | S_ISLNK(inode->i_mode)) { | 3080 | ext4_verity_in_progress(inode)) { |
3066 | *fsdata = (void *)FALL_BACK_TO_NONDELALLOC; | 3081 | *fsdata = (void *)FALL_BACK_TO_NONDELALLOC; |
3067 | return ext4_write_begin(file, mapping, pos, | 3082 | return ext4_write_begin(file, mapping, pos, |
3068 | len, flags, pagep, fsdata); | 3083 | len, flags, pagep, fsdata); |
@@ -4739,6 +4754,8 @@ static bool ext4_should_use_dax(struct inode *inode) | |||
4739 | return false; | 4754 | return false; |
4740 | if (ext4_test_inode_flag(inode, EXT4_INODE_ENCRYPT)) | 4755 | if (ext4_test_inode_flag(inode, EXT4_INODE_ENCRYPT)) |
4741 | return false; | 4756 | return false; |
4757 | if (ext4_test_inode_flag(inode, EXT4_INODE_VERITY)) | ||
4758 | return false; | ||
4742 | return true; | 4759 | return true; |
4743 | } | 4760 | } |
4744 | 4761 | ||
@@ -4763,9 +4780,11 @@ void ext4_set_inode_flags(struct inode *inode) | |||
4763 | new_fl |= S_ENCRYPTED; | 4780 | new_fl |= S_ENCRYPTED; |
4764 | if (flags & EXT4_CASEFOLD_FL) | 4781 | if (flags & EXT4_CASEFOLD_FL) |
4765 | new_fl |= S_CASEFOLD; | 4782 | new_fl |= S_CASEFOLD; |
4783 | if (flags & EXT4_VERITY_FL) | ||
4784 | new_fl |= S_VERITY; | ||
4766 | inode_set_flags(inode, new_fl, | 4785 | inode_set_flags(inode, new_fl, |
4767 | S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC|S_DAX| | 4786 | S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC|S_DAX| |
4768 | S_ENCRYPTED|S_CASEFOLD); | 4787 | S_ENCRYPTED|S_CASEFOLD|S_VERITY); |
4769 | } | 4788 | } |
4770 | 4789 | ||
4771 | static blkcnt_t ext4_inode_blocks(struct ext4_inode *raw_inode, | 4790 | static blkcnt_t ext4_inode_blocks(struct ext4_inode *raw_inode, |
@@ -5555,6 +5574,10 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) | |||
5555 | if (error) | 5574 | if (error) |
5556 | return error; | 5575 | return error; |
5557 | 5576 | ||
5577 | error = fsverity_prepare_setattr(dentry, attr); | ||
5578 | if (error) | ||
5579 | return error; | ||
5580 | |||
5558 | if (is_quota_modification(inode, attr)) { | 5581 | if (is_quota_modification(inode, attr)) { |
5559 | error = dquot_initialize(inode); | 5582 | error = dquot_initialize(inode); |
5560 | if (error) | 5583 | if (error) |
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 442f7ef873fc..ce811df71690 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c | |||
@@ -1171,6 +1171,17 @@ out: | |||
1171 | } | 1171 | } |
1172 | case EXT4_IOC_SHUTDOWN: | 1172 | case EXT4_IOC_SHUTDOWN: |
1173 | return ext4_shutdown(sb, arg); | 1173 | return ext4_shutdown(sb, arg); |
1174 | |||
1175 | case FS_IOC_ENABLE_VERITY: | ||
1176 | if (!ext4_has_feature_verity(sb)) | ||
1177 | return -EOPNOTSUPP; | ||
1178 | return fsverity_ioctl_enable(filp, (const void __user *)arg); | ||
1179 | |||
1180 | case FS_IOC_MEASURE_VERITY: | ||
1181 | if (!ext4_has_feature_verity(sb)) | ||
1182 | return -EOPNOTSUPP; | ||
1183 | return fsverity_ioctl_measure(filp, (void __user *)arg); | ||
1184 | |||
1174 | default: | 1185 | default: |
1175 | return -ENOTTY; | 1186 | return -ENOTTY; |
1176 | } | 1187 | } |
@@ -1233,6 +1244,8 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
1233 | case EXT4_IOC_GET_ENCRYPTION_POLICY: | 1244 | case EXT4_IOC_GET_ENCRYPTION_POLICY: |
1234 | case EXT4_IOC_SHUTDOWN: | 1245 | case EXT4_IOC_SHUTDOWN: |
1235 | case FS_IOC_GETFSMAP: | 1246 | case FS_IOC_GETFSMAP: |
1247 | case FS_IOC_ENABLE_VERITY: | ||
1248 | case FS_IOC_MEASURE_VERITY: | ||
1236 | break; | 1249 | break; |
1237 | default: | 1250 | default: |
1238 | return -ENOIOCTLCMD; | 1251 | return -ENOIOCTLCMD; |
diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 4079605d437a..05a9874687c3 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c | |||
@@ -1179,6 +1179,7 @@ void ext4_clear_inode(struct inode *inode) | |||
1179 | EXT4_I(inode)->jinode = NULL; | 1179 | EXT4_I(inode)->jinode = NULL; |
1180 | } | 1180 | } |
1181 | fscrypt_put_encryption_info(inode); | 1181 | fscrypt_put_encryption_info(inode); |
1182 | fsverity_cleanup_inode(inode); | ||
1182 | } | 1183 | } |
1183 | 1184 | ||
1184 | static struct inode *ext4_nfs_get_inode(struct super_block *sb, | 1185 | static struct inode *ext4_nfs_get_inode(struct super_block *sb, |
@@ -4272,6 +4273,9 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) | |||
4272 | #ifdef CONFIG_FS_ENCRYPTION | 4273 | #ifdef CONFIG_FS_ENCRYPTION |
4273 | sb->s_cop = &ext4_cryptops; | 4274 | sb->s_cop = &ext4_cryptops; |
4274 | #endif | 4275 | #endif |
4276 | #ifdef CONFIG_FS_VERITY | ||
4277 | sb->s_vop = &ext4_verityops; | ||
4278 | #endif | ||
4275 | #ifdef CONFIG_QUOTA | 4279 | #ifdef CONFIG_QUOTA |
4276 | sb->dq_op = &ext4_quota_operations; | 4280 | sb->dq_op = &ext4_quota_operations; |
4277 | if (ext4_has_feature_quota(sb)) | 4281 | if (ext4_has_feature_quota(sb)) |
@@ -4419,6 +4423,11 @@ no_journal: | |||
4419 | goto failed_mount_wq; | 4423 | goto failed_mount_wq; |
4420 | } | 4424 | } |
4421 | 4425 | ||
4426 | if (ext4_has_feature_verity(sb) && blocksize != PAGE_SIZE) { | ||
4427 | ext4_msg(sb, KERN_ERR, "Unsupported blocksize for fs-verity"); | ||
4428 | goto failed_mount_wq; | ||
4429 | } | ||
4430 | |||
4422 | if (DUMMY_ENCRYPTION_ENABLED(sbi) && !sb_rdonly(sb) && | 4431 | if (DUMMY_ENCRYPTION_ENABLED(sbi) && !sb_rdonly(sb) && |
4423 | !ext4_has_feature_encrypt(sb)) { | 4432 | !ext4_has_feature_encrypt(sb)) { |
4424 | ext4_set_feature_encrypt(sb); | 4433 | ext4_set_feature_encrypt(sb); |
diff --git a/fs/ext4/sysfs.c b/fs/ext4/sysfs.c index b3cd7655a6ff..eb1efad0e20a 100644 --- a/fs/ext4/sysfs.c +++ b/fs/ext4/sysfs.c | |||
@@ -242,6 +242,9 @@ EXT4_ATTR_FEATURE(encryption); | |||
242 | #ifdef CONFIG_UNICODE | 242 | #ifdef CONFIG_UNICODE |
243 | EXT4_ATTR_FEATURE(casefold); | 243 | EXT4_ATTR_FEATURE(casefold); |
244 | #endif | 244 | #endif |
245 | #ifdef CONFIG_FS_VERITY | ||
246 | EXT4_ATTR_FEATURE(verity); | ||
247 | #endif | ||
245 | EXT4_ATTR_FEATURE(metadata_csum_seed); | 248 | EXT4_ATTR_FEATURE(metadata_csum_seed); |
246 | 249 | ||
247 | static struct attribute *ext4_feat_attrs[] = { | 250 | static struct attribute *ext4_feat_attrs[] = { |
@@ -254,6 +257,9 @@ static struct attribute *ext4_feat_attrs[] = { | |||
254 | #ifdef CONFIG_UNICODE | 257 | #ifdef CONFIG_UNICODE |
255 | ATTR_LIST(casefold), | 258 | ATTR_LIST(casefold), |
256 | #endif | 259 | #endif |
260 | #ifdef CONFIG_FS_VERITY | ||
261 | ATTR_LIST(verity), | ||
262 | #endif | ||
257 | ATTR_LIST(metadata_csum_seed), | 263 | ATTR_LIST(metadata_csum_seed), |
258 | NULL, | 264 | NULL, |
259 | }; | 265 | }; |
diff --git a/fs/ext4/verity.c b/fs/ext4/verity.c new file mode 100644 index 000000000000..d0d8a9795dd6 --- /dev/null +++ b/fs/ext4/verity.c | |||
@@ -0,0 +1,367 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * fs/ext4/verity.c: fs-verity support for ext4 | ||
4 | * | ||
5 | * Copyright 2019 Google LLC | ||
6 | */ | ||
7 | |||
8 | /* | ||
9 | * Implementation of fsverity_operations for ext4. | ||
10 | * | ||
11 | * ext4 stores the verity metadata (Merkle tree and fsverity_descriptor) past | ||
12 | * the end of the file, starting at the first 64K boundary beyond i_size. This | ||
13 | * approach works because (a) verity files are readonly, and (b) pages fully | ||
14 | * beyond i_size aren't visible to userspace but can be read/written internally | ||
15 | * by ext4 with only some relatively small changes to ext4. This approach | ||
16 | * avoids having to depend on the EA_INODE feature and on rearchitecturing | ||
17 | * ext4's xattr support to support paging multi-gigabyte xattrs into memory, and | ||
18 | * to support encrypting xattrs. Note that the verity metadata *must* be | ||
19 | * encrypted when the file is, since it contains hashes of the plaintext data. | ||
20 | * | ||
21 | * Using a 64K boundary rather than a 4K one keeps things ready for | ||
22 | * architectures with 64K pages, and it doesn't necessarily waste space on-disk | ||
23 | * since there can be a hole between i_size and the start of the Merkle tree. | ||
24 | */ | ||
25 | |||
26 | #include <linux/quotaops.h> | ||
27 | |||
28 | #include "ext4.h" | ||
29 | #include "ext4_extents.h" | ||
30 | #include "ext4_jbd2.h" | ||
31 | |||
32 | static inline loff_t ext4_verity_metadata_pos(const struct inode *inode) | ||
33 | { | ||
34 | return round_up(inode->i_size, 65536); | ||
35 | } | ||
36 | |||
37 | /* | ||
38 | * Read some verity metadata from the inode. __vfs_read() can't be used because | ||
39 | * we need to read beyond i_size. | ||
40 | */ | ||
41 | static int pagecache_read(struct inode *inode, void *buf, size_t count, | ||
42 | loff_t pos) | ||
43 | { | ||
44 | while (count) { | ||
45 | size_t n = min_t(size_t, count, | ||
46 | PAGE_SIZE - offset_in_page(pos)); | ||
47 | struct page *page; | ||
48 | void *addr; | ||
49 | |||
50 | page = read_mapping_page(inode->i_mapping, pos >> PAGE_SHIFT, | ||
51 | NULL); | ||
52 | if (IS_ERR(page)) | ||
53 | return PTR_ERR(page); | ||
54 | |||
55 | addr = kmap_atomic(page); | ||
56 | memcpy(buf, addr + offset_in_page(pos), n); | ||
57 | kunmap_atomic(addr); | ||
58 | |||
59 | put_page(page); | ||
60 | |||
61 | buf += n; | ||
62 | pos += n; | ||
63 | count -= n; | ||
64 | } | ||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | /* | ||
69 | * Write some verity metadata to the inode for FS_IOC_ENABLE_VERITY. | ||
70 | * kernel_write() can't be used because the file descriptor is readonly. | ||
71 | */ | ||
72 | static int pagecache_write(struct inode *inode, const void *buf, size_t count, | ||
73 | loff_t pos) | ||
74 | { | ||
75 | if (pos + count > inode->i_sb->s_maxbytes) | ||
76 | return -EFBIG; | ||
77 | |||
78 | while (count) { | ||
79 | size_t n = min_t(size_t, count, | ||
80 | PAGE_SIZE - offset_in_page(pos)); | ||
81 | struct page *page; | ||
82 | void *fsdata; | ||
83 | void *addr; | ||
84 | int res; | ||
85 | |||
86 | res = pagecache_write_begin(NULL, inode->i_mapping, pos, n, 0, | ||
87 | &page, &fsdata); | ||
88 | if (res) | ||
89 | return res; | ||
90 | |||
91 | addr = kmap_atomic(page); | ||
92 | memcpy(addr + offset_in_page(pos), buf, n); | ||
93 | kunmap_atomic(addr); | ||
94 | |||
95 | res = pagecache_write_end(NULL, inode->i_mapping, pos, n, n, | ||
96 | page, fsdata); | ||
97 | if (res < 0) | ||
98 | return res; | ||
99 | if (res != n) | ||
100 | return -EIO; | ||
101 | |||
102 | buf += n; | ||
103 | pos += n; | ||
104 | count -= n; | ||
105 | } | ||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | static int ext4_begin_enable_verity(struct file *filp) | ||
110 | { | ||
111 | struct inode *inode = file_inode(filp); | ||
112 | const int credits = 2; /* superblock and inode for ext4_orphan_add() */ | ||
113 | handle_t *handle; | ||
114 | int err; | ||
115 | |||
116 | if (ext4_verity_in_progress(inode)) | ||
117 | return -EBUSY; | ||
118 | |||
119 | /* | ||
120 | * Since the file was opened readonly, we have to initialize the jbd | ||
121 | * inode and quotas here and not rely on ->open() doing it. This must | ||
122 | * be done before evicting the inline data. | ||
123 | */ | ||
124 | |||
125 | err = ext4_inode_attach_jinode(inode); | ||
126 | if (err) | ||
127 | return err; | ||
128 | |||
129 | err = dquot_initialize(inode); | ||
130 | if (err) | ||
131 | return err; | ||
132 | |||
133 | err = ext4_convert_inline_data(inode); | ||
134 | if (err) | ||
135 | return err; | ||
136 | |||
137 | if (!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) { | ||
138 | ext4_warning_inode(inode, | ||
139 | "verity is only allowed on extent-based files"); | ||
140 | return -EOPNOTSUPP; | ||
141 | } | ||
142 | |||
143 | /* | ||
144 | * ext4 uses the last allocated block to find the verity descriptor, so | ||
145 | * we must remove any other blocks past EOF which might confuse things. | ||
146 | */ | ||
147 | err = ext4_truncate(inode); | ||
148 | if (err) | ||
149 | return err; | ||
150 | |||
151 | handle = ext4_journal_start(inode, EXT4_HT_INODE, credits); | ||
152 | if (IS_ERR(handle)) | ||
153 | return PTR_ERR(handle); | ||
154 | |||
155 | err = ext4_orphan_add(handle, inode); | ||
156 | if (err == 0) | ||
157 | ext4_set_inode_state(inode, EXT4_STATE_VERITY_IN_PROGRESS); | ||
158 | |||
159 | ext4_journal_stop(handle); | ||
160 | return err; | ||
161 | } | ||
162 | |||
163 | /* | ||
164 | * ext4 stores the verity descriptor beginning on the next filesystem block | ||
165 | * boundary after the Merkle tree. Then, the descriptor size is stored in the | ||
166 | * last 4 bytes of the last allocated filesystem block --- which is either the | ||
167 | * block in which the descriptor ends, or the next block after that if there | ||
168 | * weren't at least 4 bytes remaining. | ||
169 | * | ||
170 | * We can't simply store the descriptor in an xattr because it *must* be | ||
171 | * encrypted when ext4 encryption is used, but ext4 encryption doesn't encrypt | ||
172 | * xattrs. Also, if the descriptor includes a large signature blob it may be | ||
173 | * too large to store in an xattr without the EA_INODE feature. | ||
174 | */ | ||
175 | static int ext4_write_verity_descriptor(struct inode *inode, const void *desc, | ||
176 | size_t desc_size, u64 merkle_tree_size) | ||
177 | { | ||
178 | const u64 desc_pos = round_up(ext4_verity_metadata_pos(inode) + | ||
179 | merkle_tree_size, i_blocksize(inode)); | ||
180 | const u64 desc_end = desc_pos + desc_size; | ||
181 | const __le32 desc_size_disk = cpu_to_le32(desc_size); | ||
182 | const u64 desc_size_pos = round_up(desc_end + sizeof(desc_size_disk), | ||
183 | i_blocksize(inode)) - | ||
184 | sizeof(desc_size_disk); | ||
185 | int err; | ||
186 | |||
187 | err = pagecache_write(inode, desc, desc_size, desc_pos); | ||
188 | if (err) | ||
189 | return err; | ||
190 | |||
191 | return pagecache_write(inode, &desc_size_disk, sizeof(desc_size_disk), | ||
192 | desc_size_pos); | ||
193 | } | ||
194 | |||
195 | static int ext4_end_enable_verity(struct file *filp, const void *desc, | ||
196 | size_t desc_size, u64 merkle_tree_size) | ||
197 | { | ||
198 | struct inode *inode = file_inode(filp); | ||
199 | const int credits = 2; /* superblock and inode for ext4_orphan_del() */ | ||
200 | handle_t *handle; | ||
201 | int err = 0; | ||
202 | int err2; | ||
203 | |||
204 | if (desc != NULL) { | ||
205 | /* Succeeded; write the verity descriptor. */ | ||
206 | err = ext4_write_verity_descriptor(inode, desc, desc_size, | ||
207 | merkle_tree_size); | ||
208 | |||
209 | /* Write all pages before clearing VERITY_IN_PROGRESS. */ | ||
210 | if (!err) | ||
211 | err = filemap_write_and_wait(inode->i_mapping); | ||
212 | } | ||
213 | |||
214 | /* If we failed, truncate anything we wrote past i_size. */ | ||
215 | if (desc == NULL || err) | ||
216 | ext4_truncate(inode); | ||
217 | |||
218 | /* | ||
219 | * We must always clean up by clearing EXT4_STATE_VERITY_IN_PROGRESS and | ||
220 | * deleting the inode from the orphan list, even if something failed. | ||
221 | * If everything succeeded, we'll also set the verity bit in the same | ||
222 | * transaction. | ||
223 | */ | ||
224 | |||
225 | ext4_clear_inode_state(inode, EXT4_STATE_VERITY_IN_PROGRESS); | ||
226 | |||
227 | handle = ext4_journal_start(inode, EXT4_HT_INODE, credits); | ||
228 | if (IS_ERR(handle)) { | ||
229 | ext4_orphan_del(NULL, inode); | ||
230 | return PTR_ERR(handle); | ||
231 | } | ||
232 | |||
233 | err2 = ext4_orphan_del(handle, inode); | ||
234 | if (err2) | ||
235 | goto out_stop; | ||
236 | |||
237 | if (desc != NULL && !err) { | ||
238 | struct ext4_iloc iloc; | ||
239 | |||
240 | err = ext4_reserve_inode_write(handle, inode, &iloc); | ||
241 | if (err) | ||
242 | goto out_stop; | ||
243 | ext4_set_inode_flag(inode, EXT4_INODE_VERITY); | ||
244 | ext4_set_inode_flags(inode); | ||
245 | err = ext4_mark_iloc_dirty(handle, inode, &iloc); | ||
246 | } | ||
247 | out_stop: | ||
248 | ext4_journal_stop(handle); | ||
249 | return err ?: err2; | ||
250 | } | ||
251 | |||
252 | static int ext4_get_verity_descriptor_location(struct inode *inode, | ||
253 | size_t *desc_size_ret, | ||
254 | u64 *desc_pos_ret) | ||
255 | { | ||
256 | struct ext4_ext_path *path; | ||
257 | struct ext4_extent *last_extent; | ||
258 | u32 end_lblk; | ||
259 | u64 desc_size_pos; | ||
260 | __le32 desc_size_disk; | ||
261 | u32 desc_size; | ||
262 | u64 desc_pos; | ||
263 | int err; | ||
264 | |||
265 | /* | ||
266 | * Descriptor size is in last 4 bytes of last allocated block. | ||
267 | * See ext4_write_verity_descriptor(). | ||
268 | */ | ||
269 | |||
270 | if (!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) { | ||
271 | EXT4_ERROR_INODE(inode, "verity file doesn't use extents"); | ||
272 | return -EFSCORRUPTED; | ||
273 | } | ||
274 | |||
275 | path = ext4_find_extent(inode, EXT_MAX_BLOCKS - 1, NULL, 0); | ||
276 | if (IS_ERR(path)) | ||
277 | return PTR_ERR(path); | ||
278 | |||
279 | last_extent = path[path->p_depth].p_ext; | ||
280 | if (!last_extent) { | ||
281 | EXT4_ERROR_INODE(inode, "verity file has no extents"); | ||
282 | ext4_ext_drop_refs(path); | ||
283 | kfree(path); | ||
284 | return -EFSCORRUPTED; | ||
285 | } | ||
286 | |||
287 | end_lblk = le32_to_cpu(last_extent->ee_block) + | ||
288 | ext4_ext_get_actual_len(last_extent); | ||
289 | desc_size_pos = (u64)end_lblk << inode->i_blkbits; | ||
290 | ext4_ext_drop_refs(path); | ||
291 | kfree(path); | ||
292 | |||
293 | if (desc_size_pos < sizeof(desc_size_disk)) | ||
294 | goto bad; | ||
295 | desc_size_pos -= sizeof(desc_size_disk); | ||
296 | |||
297 | err = pagecache_read(inode, &desc_size_disk, sizeof(desc_size_disk), | ||
298 | desc_size_pos); | ||
299 | if (err) | ||
300 | return err; | ||
301 | desc_size = le32_to_cpu(desc_size_disk); | ||
302 | |||
303 | /* | ||
304 | * The descriptor is stored just before the desc_size_disk, but starting | ||
305 | * on a filesystem block boundary. | ||
306 | */ | ||
307 | |||
308 | if (desc_size > INT_MAX || desc_size > desc_size_pos) | ||
309 | goto bad; | ||
310 | |||
311 | desc_pos = round_down(desc_size_pos - desc_size, i_blocksize(inode)); | ||
312 | if (desc_pos < ext4_verity_metadata_pos(inode)) | ||
313 | goto bad; | ||
314 | |||
315 | *desc_size_ret = desc_size; | ||
316 | *desc_pos_ret = desc_pos; | ||
317 | return 0; | ||
318 | |||
319 | bad: | ||
320 | EXT4_ERROR_INODE(inode, "verity file corrupted; can't find descriptor"); | ||
321 | return -EFSCORRUPTED; | ||
322 | } | ||
323 | |||
324 | static int ext4_get_verity_descriptor(struct inode *inode, void *buf, | ||
325 | size_t buf_size) | ||
326 | { | ||
327 | size_t desc_size = 0; | ||
328 | u64 desc_pos = 0; | ||
329 | int err; | ||
330 | |||
331 | err = ext4_get_verity_descriptor_location(inode, &desc_size, &desc_pos); | ||
332 | if (err) | ||
333 | return err; | ||
334 | |||
335 | if (buf_size) { | ||
336 | if (desc_size > buf_size) | ||
337 | return -ERANGE; | ||
338 | err = pagecache_read(inode, buf, desc_size, desc_pos); | ||
339 | if (err) | ||
340 | return err; | ||
341 | } | ||
342 | return desc_size; | ||
343 | } | ||
344 | |||
345 | static struct page *ext4_read_merkle_tree_page(struct inode *inode, | ||
346 | pgoff_t index) | ||
347 | { | ||
348 | index += ext4_verity_metadata_pos(inode) >> PAGE_SHIFT; | ||
349 | |||
350 | return read_mapping_page(inode->i_mapping, index, NULL); | ||
351 | } | ||
352 | |||
353 | static int ext4_write_merkle_tree_block(struct inode *inode, const void *buf, | ||
354 | u64 index, int log_blocksize) | ||
355 | { | ||
356 | loff_t pos = ext4_verity_metadata_pos(inode) + (index << log_blocksize); | ||
357 | |||
358 | return pagecache_write(inode, buf, 1 << log_blocksize, pos); | ||
359 | } | ||
360 | |||
361 | const struct fsverity_operations ext4_verityops = { | ||
362 | .begin_enable_verity = ext4_begin_enable_verity, | ||
363 | .end_enable_verity = ext4_end_enable_verity, | ||
364 | .get_verity_descriptor = ext4_get_verity_descriptor, | ||
365 | .read_merkle_tree_page = ext4_read_merkle_tree_page, | ||
366 | .write_merkle_tree_block = ext4_write_merkle_tree_block, | ||
367 | }; | ||