Received: by 2002:a05:6a10:f3d0:0:0:0:0 with SMTP id a16csp2718037pxv; Sun, 11 Jul 2021 23:41:55 -0700 (PDT) X-Google-Smtp-Source: ABdhPJy9gi9FPN8u+jGG1emexW53dRwpY1nt0yX2muxwHfqH+u55jTR8ffiQFzmkI2w40CNdScMt X-Received: by 2002:a17:906:2bd9:: with SMTP id n25mr51792050ejg.513.1626072115139; Sun, 11 Jul 2021 23:41:55 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1626072115; cv=none; d=google.com; s=arc-20160816; b=Axxrz0Ouezx5cjhBlAOK/ZY20NaVuzTqOzjJJn/sdRXa2+TVHlank4BtQq9oAmoCoZ dl2yevzdvHUUl9o/3/hmD734ccoMxaMGjF/pKilEcHIK/hrqmf7mZoWR11hQ0lBB2uVt yZJk/e1sTnAJ3rgBAjIbzHh7sOgvyrF4wkAE8MI0Cx8WAq4FKo7p1ON72VjVU5te6th+ GjDx97M/jGLRj81e6GF3Gd0HQBNmkHewviHZ38lAQ0XxJaeV1YjmXtJPkEWdcaD6ByLs kRISuwYXeJnucqrXQmuLART6Ps26raWfZh3XDrJ4kBCwxy8DvRZUw2s3WxsUaLFCbqeP mPDg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :user-agent:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=tzYINTlYazpLfU3qmFrKXaWXedCeQrLpt+gz43ep+4s=; b=vH9D0gLODdJ1eIFunB6RtheFH7TWiBQ6rXvyaUivsMUl0lwLlYYNA8cr9aAKvb8EGg gwXR5TCqKEpTJZDbOuCOexuh170Tv2XZra0G31TBldyP74l0J+2VnH8njSt9qa5jlP/W Gmc6+NIfEXygjV8BfH5FpalZju0yNngmDv847ClTIDaQGE/EIoPjwl8hPQI17Y781/Lq /RvGXzp5PbGmL9v5/jwtbhj65/10qs0/tOltuS+vt0KtR3moHkuzC0M8Au08kiq/EElG XqC1EkwJ5HAiJ61/CwCVIH7O6GiTK0MO1uJ3Lycs9aYMCgsKyATSP2akVmkcV2mDJYrJ 9d4Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linuxfoundation.org header.s=korg header.b=smiQ0Qwb; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linuxfoundation.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id j22si16543353eds.300.2021.07.11.23.41.32; Sun, 11 Jul 2021 23:41:55 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@linuxfoundation.org header.s=korg header.b=smiQ0Qwb; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linuxfoundation.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236281AbhGLGkt (ORCPT + 99 others); Mon, 12 Jul 2021 02:40:49 -0400 Received: from mail.kernel.org ([198.145.29.99]:54420 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236807AbhGLGcN (ORCPT ); Mon, 12 Jul 2021 02:32:13 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 83932610FB; Mon, 12 Jul 2021 06:29:23 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=linuxfoundation.org; s=korg; t=1626071364; bh=tyfHyiHsLENurj9i2lXHoInM47VZxNCqxt1cgwrTlgo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=smiQ0Qwbp0AItUzq6yKalrIperdrIXUpkR+pCudyNSQq2g70hGUnLI0Ix76b0A6V9 sYplpI2QAUZDwGa4ARrMhrsS+zOTKlOTehbkhvgQ+W/0B9I7JIk3kwkXTe4vyYwBJV VxJlWbzWgEyVY0QCEkIKPJ+ipPgSOsKF19eWgptg= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Filipe Manana , David Sterba Subject: [PATCH 5.10 039/593] btrfs: send: fix invalid path for unlink operations after parent orphanization Date: Mon, 12 Jul 2021 08:03:19 +0200 Message-Id: <20210712060847.486918082@linuxfoundation.org> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210712060843.180606720@linuxfoundation.org> References: <20210712060843.180606720@linuxfoundation.org> User-Agent: quilt/0.66 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Filipe Manana commit d8ac76cdd1755b21e8c008c28d0b7251c0b14986 upstream. During an incremental send operation, when processing the new references for the current inode, we might send an unlink operation for another inode that has a conflicting path and has more than one hard link. However this path was computed and cached before we processed previous new references for the current inode. We may have orphanized a directory of that path while processing a previous new reference, in which case the path will be invalid and cause the receiver process to fail. The following reproducer triggers the problem and explains how/why it happens in its comments: $ cat test-send-unlink.sh #!/bin/bash DEV=/dev/sdi MNT=/mnt/sdi mkfs.btrfs -f $DEV >/dev/null mount $DEV $MNT # Create our test files and directory. Inode 259 (file3) has two hard # links. touch $MNT/file1 touch $MNT/file2 touch $MNT/file3 mkdir $MNT/A ln $MNT/file3 $MNT/A/hard_link # Filesystem looks like: # # . (ino 256) # |----- file1 (ino 257) # |----- file2 (ino 258) # |----- file3 (ino 259) # |----- A/ (ino 260) # |---- hard_link (ino 259) # # Now create the base snapshot, which is going to be the parent snapshot # for a later incremental send. btrfs subvolume snapshot -r $MNT $MNT/snap1 btrfs send -f /tmp/snap1.send $MNT/snap1 # Move inode 257 into directory inode 260. This results in computing the # path for inode 260 as "/A" and caching it. mv $MNT/file1 $MNT/A/file1 # Move inode 258 (file2) into directory inode 260, with a name of # "hard_link", moving first inode 259 away since it currently has that # location and name. mv $MNT/A/hard_link $MNT/tmp mv $MNT/file2 $MNT/A/hard_link # Now rename inode 260 to something else (B for example) and then create # a hard link for inode 258 that has the old name and location of inode # 260 ("/A"). mv $MNT/A $MNT/B ln $MNT/B/hard_link $MNT/A # Filesystem now looks like: # # . (ino 256) # |----- tmp (ino 259) # |----- file3 (ino 259) # |----- B/ (ino 260) # | |---- file1 (ino 257) # | |---- hard_link (ino 258) # | # |----- A (ino 258) # Create another snapshot of our subvolume and use it for an incremental # send. btrfs subvolume snapshot -r $MNT $MNT/snap2 btrfs send -f /tmp/snap2.send -p $MNT/snap1 $MNT/snap2 # Now unmount the filesystem, create a new one, mount it and try to # apply both send streams to recreate both snapshots. umount $DEV mkfs.btrfs -f $DEV >/dev/null mount $DEV $MNT # First add the first snapshot to the new filesystem by applying the # first send stream. btrfs receive -f /tmp/snap1.send $MNT # The incremental receive operation below used to fail with the # following error: # # ERROR: unlink A/hard_link failed: No such file or directory # # This is because when send is processing inode 257, it generates the # path for inode 260 as "/A", since that inode is its parent in the send # snapshot, and caches that path. # # Later when processing inode 258, it first processes its new reference # that has the path of "/A", which results in orphanizing inode 260 # because there is a a path collision. This results in issuing a rename # operation from "/A" to "/o260-6-0". # # Finally when processing the new reference "B/hard_link" for inode 258, # it notices that it collides with inode 259 (not yet processed, because # it has a higher inode number), since that inode has the name # "hard_link" under the directory inode 260. It also checks that inode # 259 has two hardlinks, so it decides to issue a unlink operation for # the name "hard_link" for inode 259. However the path passed to the # unlink operation is "/A/hard_link", which is incorrect since currently # "/A" does not exists, due to the orphanization of inode 260 mentioned # before. The path is incorrect because it was computed and cached # before the orphanization. This results in the receiver to fail with # the above error. btrfs receive -f /tmp/snap2.send $MNT umount $MNT When running the test, it fails like this: $ ./test-send-unlink.sh Create a readonly snapshot of '/mnt/sdi' in '/mnt/sdi/snap1' At subvol /mnt/sdi/snap1 Create a readonly snapshot of '/mnt/sdi' in '/mnt/sdi/snap2' At subvol /mnt/sdi/snap2 At subvol snap1 At snapshot snap2 ERROR: unlink A/hard_link failed: No such file or directory Fix this by recomputing a path before issuing an unlink operation when processing the new references for the current inode if we previously have orphanized a directory. A test case for fstests will follow soon. CC: stable@vger.kernel.org # 4.4+ Signed-off-by: Filipe Manana Signed-off-by: David Sterba Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/send.c | 11 +++++++++++ 1 file changed, 11 insertions(+) --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -4080,6 +4080,17 @@ static int process_recorded_refs(struct if (ret < 0) goto out; } else { + /* + * If we previously orphanized a directory that + * collided with a new reference that we already + * processed, recompute the current path because + * that directory may be part of the path. + */ + if (orphanized_dir) { + ret = refresh_ref_path(sctx, cur); + if (ret < 0) + goto out; + } ret = send_unlink(sctx, cur->full_path); if (ret < 0) goto out;