Re: [PATCH] libbtrfsutil: fix race between subvolume iterator and deletion
From: David Sterba <hidden>
Date: 2021-07-28 11:18:10
On Tue, Jul 27, 2021 at 04:53:28PM -0700, Omar Sandoval wrote:
From: Omar Sandoval <redacted>
Subvolume iteration has a window between when we get a root ref (with
BTRFS_IOC_TREE_SEARCH or BTRFS_IOC_GET_SUBVOL_ROOTREF) and when we look
up the path of the parent directory (with BTRFS_IOC_INO_LOOKUP{,_USER}).
If the subvolume is moved or deleted and its old parent directory is
deleted during that window, then BTRFS_IOC_INO_LOOKUP{,_USER} will fail
with ENOENT. The iteration will then fail with ENOENT as well.
We originally encountered this bug with an application that called
`btrfs subvolume show` (which iterates subvolumes to find snapshots) in
parallel with other threads creating and deleting subvolumes. It can be
reproduced almost instantly with the following script:
import multiprocessing
import os
import btrfsutil
def create_and_delete_subvolume(i):
dir_name = f"subvol_iter_race{i}"
subvol_name = dir_name + "/subvol"
while True:
os.mkdir(dir_name)
btrfsutil.create_subvolume(subvol_name)
btrfsutil.delete_subvolume(subvol_name)
os.rmdir(dir_name)
def iterate_subvolumes():
fd = os.open(".", os.O_RDONLY | os.O_DIRECTORY)
while True:
with btrfsutil.SubvolumeIterator(fd, 5) as it:
for _ in it:
pass
if __name__ == "__main__":
for i in range(10):
multiprocessing.Process(target=create_and_delete_subvolume, args=(i,), daemon=True).start()
iterate_subvolumes()Can you please turn this into a test case?
Subvolume iteration should be robust against concurrent modifications to subvolumes. So, if a subvolume's parent directory no longer exists, just skip the subvolume, as it must have been deleted or moved elsewhere. Signed-off-by: Omar Sandoval <redacted>
Added to devel, thanks.