Merge tag 'f2fs-for-5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeu...
[platform/kernel/linux-starfive.git] / fs / f2fs / verity.c
index a7beff2..03549b5 100644 (file)
@@ -152,40 +152,73 @@ 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;
+       int err = 0, err2 = 0;
 
-       if (desc != NULL) {
-               /* Succeeded; write the verity descriptor. */
-               err = pagecache_write(inode, desc, desc_size, desc_pos);
+       /*
+        * If an error already occurred (which fs/verity/ signals by passing
+        * desc == NULL), then only clean-up is needed.
+        */
+       if (desc == NULL)
+               goto cleanup;
 
-               /* Write all pages before clearing FI_VERITY_IN_PROGRESS. */
-               if (!err)
-                       err = filemap_write_and_wait(inode->i_mapping);
-       }
+       /* Append the verity descriptor. */
+       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;
+
+       /* Set the verity xattr. */
+       err = f2fs_setxattr(inode, F2FS_XATTR_INDEX_VERITY,
+                           F2FS_XATTR_NAME_VERITY, &dloc, sizeof(dloc),
+                           NULL, XATTR_CREATE);
+       if (err)
+               goto cleanup;
 
-       /* If we failed, truncate anything we wrote past i_size. */
-       if (desc == NULL || err)
-               f2fs_truncate(inode);
+       /* Finally, set the verity inode flag. */
+       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;
 
-       if (desc != NULL && !err) {
-               err = f2fs_setxattr(inode, F2FS_XATTR_INDEX_VERITY,
-                                   F2FS_XATTR_NAME_VERITY, &dloc, sizeof(dloc),
-                                   NULL, XATTR_CREATE);
-               if (!err) {
-                       file_set_verity(inode);
-                       f2fs_set_inode_flags(inode);
-                       f2fs_mark_inode_dirty_sync(inode, true);
-               }
+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).
+        */
+       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);
        }
-       return err;
+       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,