+ return ret;
+}
+
+static int run_delalloc_nocow(struct inode *inode, u64 start, u64 end)
+{
+ u64 extent_start;
+ u64 extent_end;
+ u64 bytenr;
+ u64 cow_end;
+ u64 loops = 0;
+ u64 total_fs_bytes;
+ struct btrfs_root *root = BTRFS_I(inode)->root;
+ struct extent_buffer *leaf;
+ int found_type;
+ struct btrfs_path *path;
+ struct btrfs_file_extent_item *item;
+ int ret;
+ int err;
+ struct btrfs_key found_key;
+
+ total_fs_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy);
+ path = btrfs_alloc_path();
+ BUG_ON(!path);
+again:
+ ret = btrfs_lookup_file_extent(NULL, root, path,
+ inode->i_ino, start, 0);
+ if (ret < 0) {
+ btrfs_free_path(path);
+ return ret;
+ }
+
+ cow_end = end;
+ if (ret != 0) {
+ if (path->slots[0] == 0)
+ goto not_found;
+ path->slots[0]--;
+ }
+
+ leaf = path->nodes[0];
+ item = btrfs_item_ptr(leaf, path->slots[0],
+ struct btrfs_file_extent_item);
+
+ /* are we inside the extent that was found? */
+ btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
+ found_type = btrfs_key_type(&found_key);
+ if (found_key.objectid != inode->i_ino ||
+ found_type != BTRFS_EXTENT_DATA_KEY) {
+ goto not_found;
+ }
+
+ found_type = btrfs_file_extent_type(leaf, item);
+ extent_start = found_key.offset;
+ if (found_type == BTRFS_FILE_EXTENT_REG) {
+ u64 extent_num_bytes;
+
+ extent_num_bytes = btrfs_file_extent_num_bytes(leaf, item);
+ extent_end = extent_start + extent_num_bytes;
+ err = 0;
+
+ if (loops && start != extent_start)
+ goto not_found;
+
+ if (start < extent_start || start >= extent_end)
+ goto not_found;
+
+ cow_end = min(end, extent_end - 1);
+ bytenr = btrfs_file_extent_disk_bytenr(leaf, item);
+ if (bytenr == 0)
+ goto not_found;
+
+ /*
+ * we may be called by the resizer, make sure we're inside
+ * the limits of the FS
+ */
+ if (bytenr + extent_num_bytes > total_fs_bytes)
+ goto not_found;
+
+ if (btrfs_count_snapshots_in_path(root, path, bytenr) != 1) {
+ goto not_found;
+ }
+
+ start = extent_end;
+ } else {
+ goto not_found;
+ }
+loop:
+ if (start > end) {
+ btrfs_free_path(path);
+ return 0;
+ }
+ btrfs_release_path(root, path);
+ loops++;
+ goto again;
+
+not_found:
+ cow_file_range(inode, start, cow_end);
+ start = cow_end + 1;
+ goto loop;
+}
+
+static int run_delalloc_range(struct inode *inode, u64 start, u64 end)
+{
+ struct btrfs_root *root = BTRFS_I(inode)->root;
+ int ret;
+ mutex_lock(&root->fs_info->fs_mutex);
+ if (btrfs_test_opt(root, NODATACOW) ||
+ btrfs_test_flag(inode, NODATACOW))
+ ret = run_delalloc_nocow(inode, start, end);
+ else
+ ret = cow_file_range(inode, start, end);
+