Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d92581fc authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull btrfs fixes from Chris Mason:
 "These are assorted fixes, mostly from Josef nailing down xfstests
  runs.  Zach also has a long standing fix for problems with readdir
  wrapping f_pos (or ctx->pos)

  These patches were spread out over different bases, so I rebased
  things on top of rc4 and retested overnight"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux-btrfs:
  btrfs: don't loop on large offsets in readdir
  Btrfs: check to see if root_list is empty before adding it to dead roots
  Btrfs: release both paths before logging dir/changed extents
  Btrfs: allow splitting of hole em's when dropping extent cache
  Btrfs: make sure the backref walker catches all refs to our extent
  Btrfs: fix backref walking when we hit a compressed extent
  Btrfs: do not offset physical if we're compressed
  Btrfs: fix extent buffer leak after backref walking
  Btrfs: fix a bug of snapshot-aware defrag to make it work on partial extents
  btrfs: fix file truncation if FALLOC_FL_KEEP_SIZE is specified
parents b8ea0d06 db62efbb
Loading
Loading
Loading
Loading
+29 −19
Original line number Diff line number Diff line
@@ -36,9 +36,14 @@ static int check_extent_in_eb(struct btrfs_key *key, struct extent_buffer *eb,
				u64 extent_item_pos,
				struct extent_inode_elem **eie)
{
	u64 offset = 0;
	struct extent_inode_elem *e;

	if (!btrfs_file_extent_compression(eb, fi) &&
	    !btrfs_file_extent_encryption(eb, fi) &&
	    !btrfs_file_extent_other_encoding(eb, fi)) {
		u64 data_offset;
		u64 data_len;
	struct extent_inode_elem *e;

		data_offset = btrfs_file_extent_offset(eb, fi);
		data_len = btrfs_file_extent_num_bytes(eb, fi);
@@ -46,6 +51,8 @@ static int check_extent_in_eb(struct btrfs_key *key, struct extent_buffer *eb,
		if (extent_item_pos < data_offset ||
		    extent_item_pos >= data_offset + data_len)
			return 1;
		offset = extent_item_pos - data_offset;
	}

	e = kmalloc(sizeof(*e), GFP_NOFS);
	if (!e)
@@ -53,7 +60,7 @@ static int check_extent_in_eb(struct btrfs_key *key, struct extent_buffer *eb,

	e->next = *eie;
	e->inum = key->objectid;
	e->offset = key->offset + (extent_item_pos - data_offset);
	e->offset = key->offset + offset;
	*eie = e;

	return 0;
@@ -189,7 +196,7 @@ static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path,
	struct extent_buffer *eb;
	struct btrfs_key key;
	struct btrfs_file_extent_item *fi;
	struct extent_inode_elem *eie = NULL;
	struct extent_inode_elem *eie = NULL, *old = NULL;
	u64 disk_byte;

	if (level != 0) {
@@ -223,6 +230,7 @@ static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path,

		if (disk_byte == wanted_disk_byte) {
			eie = NULL;
			old = NULL;
			if (extent_item_pos) {
				ret = check_extent_in_eb(&key, eb, fi,
						*extent_item_pos,
@@ -230,18 +238,20 @@ static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path,
				if (ret < 0)
					break;
			}
			if (!ret) {
				ret = ulist_add(parents, eb->start,
						(uintptr_t)eie, GFP_NOFS);
			if (ret > 0)
				goto next;
			ret = ulist_add_merge(parents, eb->start,
					      (uintptr_t)eie,
					      (u64 *)&old, GFP_NOFS);
			if (ret < 0)
				break;
				if (!extent_item_pos) {
					ret = btrfs_next_old_leaf(root, path,
							time_seq);
					continue;
				}
			if (!ret && extent_item_pos) {
				while (old->next)
					old = old->next;
				old->next = eie;
			}
		}
next:
		ret = btrfs_next_old_item(root, path, time_seq);
	}

+0 −1
Original line number Diff line number Diff line
@@ -1271,7 +1271,6 @@ tree_mod_log_rewind(struct btrfs_fs_info *fs_info, struct extent_buffer *eb,
		BUG_ON(!eb_rewin);
	}

	extent_buffer_get(eb_rewin);
	btrfs_tree_read_unlock(eb);
	free_extent_buffer(eb);

+6 −3
Original line number Diff line number Diff line
@@ -4048,7 +4048,7 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
	}

	while (!end) {
		u64 offset_in_extent;
		u64 offset_in_extent = 0;

		/* break if the extent we found is outside the range */
		if (em->start >= max || extent_map_end(em) < off)
@@ -4064,8 +4064,11 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,

		/*
		 * record the offset from the start of the extent
		 * for adjusting the disk offset below
		 * for adjusting the disk offset below.  Only do this if the
		 * extent isn't compressed since our in ram offset may be past
		 * what we have actually allocated on disk.
		 */
		if (!test_bit(EXTENT_FLAG_COMPRESSED, &em->flags))
			offset_in_extent = em_start - em->start;
		em_end = extent_map_end(em);
		em_len = em_end - em_start;
+40 −22
Original line number Diff line number Diff line
@@ -596,10 +596,11 @@ void btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end,
		if (no_splits)
			goto next;

		if (em->block_start < EXTENT_MAP_LAST_BYTE &&
		    em->start < start) {
		if (em->start < start) {
			split->start = em->start;
			split->len = start - em->start;

			if (em->block_start < EXTENT_MAP_LAST_BYTE) {
				split->orig_start = em->orig_start;
				split->block_start = em->block_start;

@@ -607,9 +608,17 @@ void btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end,
					split->block_len = em->block_len;
				else
					split->block_len = split->len;
			split->ram_bytes = em->ram_bytes;
				split->orig_block_len = max(split->block_len,
						em->orig_block_len);
				split->ram_bytes = em->ram_bytes;
			} else {
				split->orig_start = split->start;
				split->block_len = 0;
				split->block_start = em->block_start;
				split->orig_block_len = 0;
				split->ram_bytes = split->len;
			}

			split->generation = gen;
			split->bdev = em->bdev;
			split->flags = flags;
@@ -620,8 +629,7 @@ void btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end,
			split = split2;
			split2 = NULL;
		}
		if (em->block_start < EXTENT_MAP_LAST_BYTE &&
		    testend && em->start + em->len > start + len) {
		if (testend && em->start + em->len > start + len) {
			u64 diff = start + len - em->start;

			split->start = start + len;
@@ -630,19 +638,29 @@ void btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end,
			split->flags = flags;
			split->compress_type = em->compress_type;
			split->generation = gen;

			if (em->block_start < EXTENT_MAP_LAST_BYTE) {
				split->orig_block_len = max(em->block_len,
						    em->orig_block_len);
			split->ram_bytes = em->ram_bytes;

				split->ram_bytes = em->ram_bytes;
				if (compressed) {
					split->block_len = em->block_len;
					split->block_start = em->block_start;
					split->orig_start = em->orig_start;
				} else {
					split->block_len = split->len;
				split->block_start = em->block_start + diff;
					split->block_start = em->block_start
						+ diff;
					split->orig_start = em->orig_start;
				}
			} else {
				split->ram_bytes = split->len;
				split->orig_start = split->start;
				split->block_len = 0;
				split->block_start = em->block_start;
				split->orig_block_len = 0;
			}

			ret = add_extent_mapping(em_tree, split, modified);
			BUG_ON(ret); /* Logic error */
+37 −15
Original line number Diff line number Diff line
@@ -2166,16 +2166,23 @@ static noinline int record_one_backref(u64 inum, u64 offset, u64 root_id,
		if (btrfs_file_extent_disk_bytenr(leaf, extent) != old->bytenr)
			continue;

		extent_offset = btrfs_file_extent_offset(leaf, extent);
		if (key.offset - extent_offset != offset)
		/*
		 * 'offset' refers to the exact key.offset,
		 * NOT the 'offset' field in btrfs_extent_data_ref, ie.
		 * (key.offset - extent_offset).
		 */
		if (key.offset != offset)
			continue;

		extent_offset = btrfs_file_extent_offset(leaf, extent);
		num_bytes = btrfs_file_extent_num_bytes(leaf, extent);

		if (extent_offset >= old->extent_offset + old->offset +
		    old->len || extent_offset + num_bytes <=
		    old->extent_offset + old->offset)
			continue;

		ret = 0;
		break;
	}

@@ -2187,7 +2194,7 @@ static noinline int record_one_backref(u64 inum, u64 offset, u64 root_id,

	backref->root_id = root_id;
	backref->inum = inum;
	backref->file_pos = offset + extent_offset;
	backref->file_pos = offset;
	backref->num_bytes = num_bytes;
	backref->extent_offset = extent_offset;
	backref->generation = btrfs_file_extent_generation(leaf, extent);
@@ -2210,7 +2217,8 @@ static noinline bool record_extent_backrefs(struct btrfs_path *path,
	new->path = path;

	list_for_each_entry_safe(old, tmp, &new->head, list) {
		ret = iterate_inodes_from_logical(old->bytenr, fs_info,
		ret = iterate_inodes_from_logical(old->bytenr +
						  old->extent_offset, fs_info,
						  path, record_one_backref,
						  old);
		BUG_ON(ret < 0 && ret != -ENOENT);
@@ -4391,9 +4399,6 @@ static int btrfs_setsize(struct inode *inode, struct iattr *attr)
	int mask = attr->ia_valid;
	int ret;

	if (newsize == oldsize)
		return 0;

	/*
	 * The regular truncate() case without ATTR_CTIME and ATTR_MTIME is a
	 * special case where we need to update the times despite not having
@@ -5165,14 +5170,31 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)
	}

	/* Reached end of directory/root. Bump pos past the last item. */
	if (key_type == BTRFS_DIR_INDEX_KEY)
	ctx->pos++;

	/*
		 * 32-bit glibc will use getdents64, but then strtol -
		 * so the last number we can serve is this.
	 * Stop new entries from being returned after we return the last
	 * entry.
	 *
	 * New directory entries are assigned a strictly increasing
	 * offset.  This means that new entries created during readdir
	 * are *guaranteed* to be seen in the future by that readdir.
	 * This has broken buggy programs which operate on names as
	 * they're returned by readdir.  Until we re-use freed offsets
	 * we have this hack to stop new entries from being returned
	 * under the assumption that they'll never reach this huge
	 * offset.
	 *
	 * This is being careful not to overflow 32bit loff_t unless the
	 * last entry requires it because doing so has broken 32bit apps
	 * in the past.
	 */
		ctx->pos = 0x7fffffff;
	if (key_type == BTRFS_DIR_INDEX_KEY) {
		if (ctx->pos >= INT_MAX)
			ctx->pos = LLONG_MAX;
		else
		ctx->pos++;
			ctx->pos = INT_MAX;
	}
nopos:
	ret = 0;
err:
Loading