]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/blobdiff - fs/ext4/inode.c
Merge tag 'fsverity-for-linus' of git://git.kernel.org/pub/scm/fs/fscrypt/fscrypt
[mirror_ubuntu-focal-kernel.git] / fs / ext4 / inode.c
index 006b7a2070bf6869d2f7c42500eea135919def74..d0dc0e3463db83bea66fb14540d0275ceadd0347 100644 (file)
@@ -1340,6 +1340,9 @@ retry_journal:
        }
 
        if (ret) {
+               bool extended = (pos + len > inode->i_size) &&
+                               !ext4_verity_in_progress(inode);
+
                unlock_page(page);
                /*
                 * __block_write_begin may have instantiated a few blocks
@@ -1349,11 +1352,11 @@ retry_journal:
                 * Add inode to orphan list in case we crash before
                 * truncate finishes
                 */
-               if (pos + len > inode->i_size && ext4_can_truncate(inode))
+               if (extended && ext4_can_truncate(inode))
                        ext4_orphan_add(handle, inode);
 
                ext4_journal_stop(handle);
-               if (pos + len > inode->i_size) {
+               if (extended) {
                        ext4_truncate_failed_write(inode);
                        /*
                         * If truncate failed early the inode might
@@ -1406,6 +1409,7 @@ static int ext4_write_end(struct file *file,
        int ret = 0, ret2;
        int i_size_changed = 0;
        int inline_data = ext4_has_inline_data(inode);
+       bool verity = ext4_verity_in_progress(inode);
 
        trace_ext4_write_end(inode, pos, len, copied);
        if (inline_data) {
@@ -1423,12 +1427,16 @@ static int ext4_write_end(struct file *file,
        /*
         * it's important to update i_size while still holding page lock:
         * page writeout could otherwise come in and zero beyond i_size.
+        *
+        * If FS_IOC_ENABLE_VERITY is running on this inode, then Merkle tree
+        * blocks are being written past EOF, so skip the i_size update.
         */
-       i_size_changed = ext4_update_inode_size(inode, pos + copied);
+       if (!verity)
+               i_size_changed = ext4_update_inode_size(inode, pos + copied);
        unlock_page(page);
        put_page(page);
 
-       if (old_size < pos)
+       if (old_size < pos && !verity)
                pagecache_isize_extended(inode, old_size, pos);
        /*
         * Don't mark the inode dirty under page lock. First, it unnecessarily
@@ -1439,7 +1447,7 @@ static int ext4_write_end(struct file *file,
        if (i_size_changed || inline_data)
                ext4_mark_inode_dirty(handle, inode);
 
-       if (pos + len > inode->i_size && ext4_can_truncate(inode))
+       if (pos + len > inode->i_size && !verity && ext4_can_truncate(inode))
                /* if we have allocated more blocks and copied
                 * less. We will have blocks allocated outside
                 * inode->i_size. So truncate them
@@ -1450,7 +1458,7 @@ errout:
        if (!ret)
                ret = ret2;
 
-       if (pos + len > inode->i_size) {
+       if (pos + len > inode->i_size && !verity) {
                ext4_truncate_failed_write(inode);
                /*
                 * If truncate failed early the inode might still be
@@ -1511,6 +1519,7 @@ static int ext4_journalled_write_end(struct file *file,
        unsigned from, to;
        int size_changed = 0;
        int inline_data = ext4_has_inline_data(inode);
+       bool verity = ext4_verity_in_progress(inode);
 
        trace_ext4_journalled_write_end(inode, pos, len, copied);
        from = pos & (PAGE_SIZE - 1);
@@ -1540,13 +1549,14 @@ static int ext4_journalled_write_end(struct file *file,
                if (!partial)
                        SetPageUptodate(page);
        }
-       size_changed = ext4_update_inode_size(inode, pos + copied);
+       if (!verity)
+               size_changed = ext4_update_inode_size(inode, pos + copied);
        ext4_set_inode_state(inode, EXT4_STATE_JDATA);
        EXT4_I(inode)->i_datasync_tid = handle->h_transaction->t_tid;
        unlock_page(page);
        put_page(page);
 
-       if (old_size < pos)
+       if (old_size < pos && !verity)
                pagecache_isize_extended(inode, old_size, pos);
 
        if (size_changed || inline_data) {
@@ -1555,7 +1565,7 @@ static int ext4_journalled_write_end(struct file *file,
                        ret = ret2;
        }
 
-       if (pos + len > inode->i_size && ext4_can_truncate(inode))
+       if (pos + len > inode->i_size && !verity && ext4_can_truncate(inode))
                /* if we have allocated more blocks and copied
                 * less. We will have blocks allocated outside
                 * inode->i_size. So truncate them
@@ -1566,7 +1576,7 @@ errout:
        ret2 = ext4_journal_stop(handle);
        if (!ret)
                ret = ret2;
-       if (pos + len > inode->i_size) {
+       if (pos + len > inode->i_size && !verity) {
                ext4_truncate_failed_write(inode);
                /*
                 * If truncate failed early the inode might still be
@@ -2162,7 +2172,8 @@ static int ext4_writepage(struct page *page,
 
        trace_ext4_writepage(page);
        size = i_size_read(inode);
-       if (page->index == size >> PAGE_SHIFT)
+       if (page->index == size >> PAGE_SHIFT &&
+           !ext4_verity_in_progress(inode))
                len = size & ~PAGE_MASK;
        else
                len = PAGE_SIZE;
@@ -2246,7 +2257,8 @@ static int mpage_submit_page(struct mpage_da_data *mpd, struct page *page)
         * after page tables are updated.
         */
        size = i_size_read(mpd->inode);
-       if (page->index == size >> PAGE_SHIFT)
+       if (page->index == size >> PAGE_SHIFT &&
+           !ext4_verity_in_progress(mpd->inode))
                len = size & ~PAGE_MASK;
        else
                len = PAGE_SIZE;
@@ -2345,6 +2357,9 @@ static int mpage_process_page_bufs(struct mpage_da_data *mpd,
        ext4_lblk_t blocks = (i_size_read(inode) + i_blocksize(inode) - 1)
                                                        >> inode->i_blkbits;
 
+       if (ext4_verity_in_progress(inode))
+               blocks = EXT_MAX_BLOCKS;
+
        do {
                BUG_ON(buffer_locked(bh));
 
@@ -3061,8 +3076,8 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping,
 
        index = pos >> PAGE_SHIFT;
 
-       if (ext4_nonda_switch(inode->i_sb) ||
-           S_ISLNK(inode->i_mode)) {
+       if (ext4_nonda_switch(inode->i_sb) || S_ISLNK(inode->i_mode) ||
+           ext4_verity_in_progress(inode)) {
                *fsdata = (void *)FALL_BACK_TO_NONDELALLOC;
                return ext4_write_begin(file, mapping, pos,
                                        len, flags, pagep, fsdata);
@@ -3897,6 +3912,8 @@ static ssize_t ext4_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
        if (IS_ENCRYPTED(inode) && S_ISREG(inode->i_mode))
                return 0;
 #endif
+       if (fsverity_active(inode))
+               return 0;
 
        /*
         * If we are doing data journalling we don't support O_DIRECT
@@ -4736,6 +4753,8 @@ static bool ext4_should_use_dax(struct inode *inode)
                return false;
        if (ext4_test_inode_flag(inode, EXT4_INODE_ENCRYPT))
                return false;
+       if (ext4_test_inode_flag(inode, EXT4_INODE_VERITY))
+               return false;
        return true;
 }
 
@@ -4760,9 +4779,11 @@ void ext4_set_inode_flags(struct inode *inode)
                new_fl |= S_ENCRYPTED;
        if (flags & EXT4_CASEFOLD_FL)
                new_fl |= S_CASEFOLD;
+       if (flags & EXT4_VERITY_FL)
+               new_fl |= S_VERITY;
        inode_set_flags(inode, new_fl,
                        S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC|S_DAX|
-                       S_ENCRYPTED|S_CASEFOLD);
+                       S_ENCRYPTED|S_CASEFOLD|S_VERITY);
 }
 
 static blkcnt_t ext4_inode_blocks(struct ext4_inode *raw_inode,
@@ -5552,6 +5573,10 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
        if (error)
                return error;
 
+       error = fsverity_prepare_setattr(dentry, attr);
+       if (error)
+               return error;
+
        if (is_quota_modification(inode, attr)) {
                error = dquot_initialize(inode);
                if (error)