Merge tag 'hsi-for-3.19' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-hsi
[linux-2.6-block.git] / fs / btrfs / inode.c
index 01d223e22bb146c6d310e47da0cbdb6eed7268b8..e687bb0dc73a36724a921d40bc66c89473f7edac 100644 (file)
@@ -527,7 +527,10 @@ cont:
                if (ret <= 0) {
                        unsigned long clear_flags = EXTENT_DELALLOC |
                                EXTENT_DEFRAG;
+                       unsigned long page_error_op;
+
                        clear_flags |= (ret < 0) ? EXTENT_DO_ACCOUNTING : 0;
+                       page_error_op = ret < 0 ? PAGE_SET_ERROR : 0;
 
                        /*
                         * inline extent creation worked or returned error,
@@ -538,6 +541,7 @@ cont:
                                                     clear_flags, PAGE_UNLOCK |
                                                     PAGE_CLEAR_DIRTY |
                                                     PAGE_SET_WRITEBACK |
+                                                    page_error_op |
                                                     PAGE_END_WRITEBACK);
                        goto free_pages_out;
                }
@@ -1333,7 +1337,7 @@ next_slot:
                         * we fall into common COW way.
                         */
                        if (!nolock) {
-                               err = btrfs_start_nocow_write(root);
+                               err = btrfs_start_write_no_snapshoting(root);
                                if (!err)
                                        goto out_check;
                        }
@@ -1357,7 +1361,7 @@ out_check:
                if (extent_end <= start) {
                        path->slots[0]++;
                        if (!nolock && nocow)
-                               btrfs_end_nocow_write(root);
+                               btrfs_end_write_no_snapshoting(root);
                        goto next_slot;
                }
                if (!nocow) {
@@ -1377,7 +1381,7 @@ out_check:
                                             page_started, nr_written, 1);
                        if (ret) {
                                if (!nolock && nocow)
-                                       btrfs_end_nocow_write(root);
+                                       btrfs_end_write_no_snapshoting(root);
                                goto error;
                        }
                        cow_start = (u64)-1;
@@ -1428,7 +1432,7 @@ out_check:
                                                      num_bytes);
                        if (ret) {
                                if (!nolock && nocow)
-                                       btrfs_end_nocow_write(root);
+                                       btrfs_end_write_no_snapshoting(root);
                                goto error;
                        }
                }
@@ -1439,7 +1443,7 @@ out_check:
                                             EXTENT_DELALLOC, PAGE_UNLOCK |
                                             PAGE_SET_PRIVATE2);
                if (!nolock && nocow)
-                       btrfs_end_nocow_write(root);
+                       btrfs_end_write_no_snapshoting(root);
                cur_offset = extent_end;
                if (cur_offset > end)
                        break;
@@ -4595,6 +4599,26 @@ next:
        return err;
 }
 
+static int wait_snapshoting_atomic_t(atomic_t *a)
+{
+       schedule();
+       return 0;
+}
+
+static void wait_for_snapshot_creation(struct btrfs_root *root)
+{
+       while (true) {
+               int ret;
+
+               ret = btrfs_start_write_no_snapshoting(root);
+               if (ret)
+                       break;
+               wait_on_atomic_t(&root->will_be_snapshoted,
+                                wait_snapshoting_atomic_t,
+                                TASK_UNINTERRUPTIBLE);
+       }
+}
+
 static int btrfs_setsize(struct inode *inode, struct iattr *attr)
 {
        struct btrfs_root *root = BTRFS_I(inode)->root;
@@ -4619,17 +4643,30 @@ static int btrfs_setsize(struct inode *inode, struct iattr *attr)
 
        if (newsize > oldsize) {
                truncate_pagecache(inode, newsize);
+               /*
+                * Don't do an expanding truncate while snapshoting is ongoing.
+                * This is to ensure the snapshot captures a fully consistent
+                * state of this file - if the snapshot captures this expanding
+                * truncation, it must capture all writes that happened before
+                * this truncation.
+                */
+               wait_for_snapshot_creation(root);
                ret = btrfs_cont_expand(inode, oldsize, newsize);
-               if (ret)
+               if (ret) {
+                       btrfs_end_write_no_snapshoting(root);
                        return ret;
+               }
 
                trans = btrfs_start_transaction(root, 1);
-               if (IS_ERR(trans))
+               if (IS_ERR(trans)) {
+                       btrfs_end_write_no_snapshoting(root);
                        return PTR_ERR(trans);
+               }
 
                i_size_write(inode, newsize);
                btrfs_ordered_update_i_size(inode, i_size_read(inode), NULL);
                ret = btrfs_update_inode(trans, root, inode);
+               btrfs_end_write_no_snapshoting(root);
                btrfs_end_transaction(trans, root);
        } else {
 
@@ -5318,7 +5355,7 @@ static struct dentry *btrfs_lookup(struct inode *dir, struct dentry *dentry,
                        return ERR_CAST(inode);
        }
 
-       return d_materialise_unique(dentry, inode);
+       return d_splice_alias(inode, dentry);
 }
 
 unsigned char btrfs_filetype_table[] = {
@@ -7015,14 +7052,7 @@ static int lock_extent_direct(struct inode *inode, u64 lockstart, u64 lockend,
                        btrfs_put_ordered_extent(ordered);
                } else {
                        /* Screw you mmap */
-                       ret = filemap_fdatawrite_range(inode->i_mapping,
-                                                      lockstart,
-                                                      lockend);
-                       if (!ret && test_bit(BTRFS_INODE_HAS_ASYNC_EXTENT,
-                                            &BTRFS_I(inode)->runtime_flags))
-                               ret = filemap_fdatawrite_range(inode->i_mapping,
-                                                              lockstart,
-                                                              lockend);
+                       ret = btrfs_fdatawrite_range(inode, lockstart, lockend);
                        if (ret)
                                break;
                        ret = filemap_fdatawait_range(inode->i_mapping,
@@ -9467,6 +9497,21 @@ out_inode:
 
 }
 
+/* Inspired by filemap_check_errors() */
+int btrfs_inode_check_errors(struct inode *inode)
+{
+       int ret = 0;
+
+       if (test_bit(AS_ENOSPC, &inode->i_mapping->flags) &&
+           test_and_clear_bit(AS_ENOSPC, &inode->i_mapping->flags))
+               ret = -ENOSPC;
+       if (test_bit(AS_EIO, &inode->i_mapping->flags) &&
+           test_and_clear_bit(AS_EIO, &inode->i_mapping->flags))
+               ret = -EIO;
+
+       return ret;
+}
+
 static const struct inode_operations btrfs_dir_inode_operations = {
        .getattr        = btrfs_getattr,
        .lookup         = btrfs_lookup,