fuse: fix deadlock between atomic O_TRUNC and page invalidation
[sfrench/cifs-2.6.git] / fs / fuse / file.c
index 60885ff9157cb5113d30b508ff051c6c2b46fd03..dfee142bca5c6b5c8620a87f6c0b91142a42ea72 100644 (file)
@@ -210,13 +210,9 @@ void fuse_finish_open(struct inode *inode, struct file *file)
                fi->attr_version = atomic64_inc_return(&fc->attr_version);
                i_size_write(inode, 0);
                spin_unlock(&fi->lock);
-               truncate_pagecache(inode, 0);
                file_update_time(file);
                fuse_invalidate_attr_mask(inode, FUSE_STATX_MODSIZE);
-       } else if (!(ff->open_flags & FOPEN_KEEP_CACHE)) {
-               invalidate_inode_pages2(inode->i_mapping);
        }
-
        if ((file->f_mode & FMODE_WRITE) && fc->writeback_cache)
                fuse_link_write_file(file);
 }
@@ -239,30 +235,38 @@ int fuse_open_common(struct inode *inode, struct file *file, bool isdir)
        if (err)
                return err;
 
-       if (is_wb_truncate || dax_truncate) {
+       if (is_wb_truncate || dax_truncate)
                inode_lock(inode);
-               fuse_set_nowrite(inode);
-       }
 
        if (dax_truncate) {
                filemap_invalidate_lock(inode->i_mapping);
                err = fuse_dax_break_layouts(inode, 0, 0);
                if (err)
-                       goto out;
+                       goto out_inode_unlock;
        }
 
+       if (is_wb_truncate || dax_truncate)
+               fuse_set_nowrite(inode);
+
        err = fuse_do_open(fm, get_node_id(inode), file, isdir);
        if (!err)
                fuse_finish_open(inode, file);
 
-out:
+       if (is_wb_truncate || dax_truncate)
+               fuse_release_nowrite(inode);
+       if (!err) {
+               struct fuse_file *ff = file->private_data;
+
+               if (fc->atomic_o_trunc && (file->f_flags & O_TRUNC))
+                       truncate_pagecache(inode, 0);
+               else if (!(ff->open_flags & FOPEN_KEEP_CACHE))
+                       invalidate_inode_pages2(inode->i_mapping);
+       }
        if (dax_truncate)
                filemap_invalidate_unlock(inode->i_mapping);
-
-       if (is_wb_truncate | dax_truncate) {
-               fuse_release_nowrite(inode);
+out_inode_unlock:
+       if (is_wb_truncate || dax_truncate)
                inode_unlock(inode);
-       }
 
        return err;
 }