Commit cca7a0aa authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull btrfs fix from David Sterba:
 "A single btrfs commit. It fixes a problem that people started to hit
  since 6.15.3 during log replay (e.g. after a crash).

  The bug is old but got more likely to happen since commit
  5e85262e ("btrfs: fix fsync of files with no hard links not
  persisting deletion") got backported to stable (6.15 only)"

* tag 'for-6.17-fix-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
  btrfs: fix log tree replay failure due to file with 0 links and extents
parents d7edcc7c 0a32e4f0
Loading
Loading
Loading
Loading
+30 −18
Original line number Diff line number Diff line
@@ -321,8 +321,7 @@ struct walk_control {

	/*
	 * Ignore any items from the inode currently being processed. Needs
	 * to be set every time we find a BTRFS_INODE_ITEM_KEY and we are in
	 * the LOG_WALK_REPLAY_INODES stage.
	 * to be set every time we find a BTRFS_INODE_ITEM_KEY.
	 */
	bool ignore_cur_inode;

@@ -2465,23 +2464,30 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,

	nritems = btrfs_header_nritems(eb);
	for (i = 0; i < nritems; i++) {
		btrfs_item_key_to_cpu(eb, &key, i);

		/* inode keys are done during the first stage */
		if (key.type == BTRFS_INODE_ITEM_KEY &&
		    wc->stage == LOG_WALK_REPLAY_INODES) {
		struct btrfs_inode_item *inode_item;
			u32 mode;

			inode_item = btrfs_item_ptr(eb, i,
					    struct btrfs_inode_item);
		btrfs_item_key_to_cpu(eb, &key, i);

		if (key.type == BTRFS_INODE_ITEM_KEY) {
			inode_item = btrfs_item_ptr(eb, i, struct btrfs_inode_item);
			/*
			 * If we have a tmpfile (O_TMPFILE) that got fsync'ed
			 * and never got linked before the fsync, skip it, as
			 * replaying it is pointless since it would be deleted
			 * later. We skip logging tmpfiles, but it's always
			 * possible we are replaying a log created with a kernel
			 * that used to log tmpfiles.
			 * An inode with no links is either:
			 *
			 * 1) A tmpfile (O_TMPFILE) that got fsync'ed and never
			 *    got linked before the fsync, skip it, as replaying
			 *    it is pointless since it would be deleted later.
			 *    We skip logging tmpfiles, but it's always possible
			 *    we are replaying a log created with a kernel that
			 *    used to log tmpfiles;
			 *
			 * 2) A non-tmpfile which got its last link deleted
			 *    while holding an open fd on it and later got
			 *    fsynced through that fd. We always log the
			 *    parent inodes when inode->last_unlink_trans is
			 *    set to the current transaction, so ignore all the
			 *    inode items for this inode. We will delete the
			 *    inode when processing the parent directory with
			 *    replay_dir_deletes().
			 */
			if (btrfs_inode_nlink(eb, inode_item) == 0) {
				wc->ignore_cur_inode = true;
@@ -2489,8 +2495,14 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,
			} else {
				wc->ignore_cur_inode = false;
			}
			ret = replay_xattr_deletes(wc->trans, root, log,
						   path, key.objectid);
		}

		/* Inode keys are done during the first stage. */
		if (key.type == BTRFS_INODE_ITEM_KEY &&
		    wc->stage == LOG_WALK_REPLAY_INODES) {
			u32 mode;

			ret = replay_xattr_deletes(wc->trans, root, log, path, key.objectid);
			if (ret)
				break;
			mode = btrfs_inode_mode(eb, inode_item);