From 6094201431aa981c6e0d149b6d528bc4b7a5af91 Mon Sep 17 00:00:00 2001
From: Qu Wenruo <wqu@suse.com>
Date: Thu, 16 Jan 2025 19:56:02 +1030
Subject: [PATCH] btrfs-progs: remove loopback device resolution

[BUG]
mkfs.btrfs has a built-in loopback device resolution, to avoid the same
file being added to the same fs, using loopback device and the file
itself.

But it has one big bug:

- It doesn't detect partition on loopback devices correctly
  The function is_loop_device() only utilize major number to detect a
  loopback device.
  But partitions on loopback devices doesn't use the same major number
  as the loopback device:

  NAME            MAJ:MIN RM  SIZE RO TYPE  MOUNTPOINTS
  loop0             7:0    0    5G  0 loop
  |-loop0p1       259:3    0  128M  0 part
  `-loop0p2       259:4    0  4.9G  0 part

  Thus `/dev/loop0p1` will not be treated as a loopback device, thus it
  will not even resolve the source file.

  And this can not even be fixed, as if we do extra "/dev/loop*" based
  file lookup, `/dev/loop0p1` and `/dev/loop0p2` will resolve to the
  same source file, and refuse to mkfs on two different partitions.

[FIX]
The loopback file detection is the baby sitting that no one asks for.

Just as I explained, it only brings new bugs, and we will never fix all
ways that an experienced user can come up with.
And I didn't see any other mkfs tool doing such baby sitting.

So remove the loopback file resolution, just regular is_same_blk_file()
is good enough.

Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 common/open-utils.c |   7 ++-
 common/path-utils.c | 127 +-------------------------------------------
 common/path-utils.h |   2 +-
 3 files changed, 5 insertions(+), 131 deletions(-)

diff --git a/common/open-utils.c b/common/open-utils.c
index 8490be4af0..a292177691 100644
--- a/common/open-utils.c
+++ b/common/open-utils.c
@@ -36,8 +36,7 @@
 #include "common/open-utils.h"
 
 /*
- * Check if a file is used (directly or indirectly via a loop device) by a
- * device in fs_devices
+ * Check if a file is used  by a device in fs_devices
  */
 static int blk_file_in_dev_list(struct btrfs_fs_devices* fs_devices,
 		const char* file)
@@ -46,7 +45,7 @@ static int blk_file_in_dev_list(struct btrfs_fs_devices* fs_devices,
 	struct btrfs_device *device;
 
 	list_for_each_entry(device, &fs_devices->devices, dev_list) {
-		if((ret = is_same_loop_file(device->name, file)))
+		if((ret = is_same_blk_file(device->name, file)))
 			return ret;
 	}
 
@@ -94,7 +93,7 @@ int check_mounted_where(int fd, const char *file, char *where, int size,
 			else if(!ret)
 				continue;
 
-			ret = is_same_loop_file(file, mnt->mnt_fsname);
+			ret = is_same_blk_file(file, mnt->mnt_fsname);
 		}
 
 		if(ret < 0)
diff --git a/common/path-utils.c b/common/path-utils.c
index 04861d1668..3030ddc47f 100644
--- a/common/path-utils.c
+++ b/common/path-utils.c
@@ -107,87 +107,11 @@ int path_exists(const char *path)
 	return 1;
 }
 
-/* checks if a device is a loop device */
-static int is_loop_device(const char *device)
-{
-	struct stat statbuf;
-
-	if(stat(device, &statbuf) < 0)
-		return -errno;
-
-	return (S_ISBLK(statbuf.st_mode) &&
-		MAJOR(statbuf.st_rdev) == LOOP_MAJOR);
-}
-
-/*
- * Takes a loop device path (e.g. /dev/loop0) and returns
- * the associated file (e.g. /images/my_btrfs.img) using
- * loopdev API
- */
-static int resolve_loop_device_with_loopdev(const char* loop_dev, char* loop_file)
-{
-	int fd;
-	int ret;
-	struct loop_info64 lo64;
-
-	fd = open(loop_dev, O_RDONLY | O_NONBLOCK);
-	if (fd < 0)
-		return -errno;
-	ret = ioctl(fd, LOOP_GET_STATUS64, &lo64);
-	if (ret < 0) {
-		ret = -errno;
-		goto out;
-	}
-
-	memcpy(loop_file, lo64.lo_file_name, sizeof(lo64.lo_file_name));
-	loop_file[sizeof(lo64.lo_file_name)] = 0;
-
-out:
-	close(fd);
-
-	return ret;
-}
-
-/*
- * Takes a loop device path (e.g. /dev/loop0) and returns
- * the associated file (e.g. /images/my_btrfs.img)
- */
-static int resolve_loop_device(const char* loop_dev, char* loop_file,
-		int max_len)
-{
-	int ret;
-	FILE *f;
-	char fmt[20];
-	char p[PATH_MAX];
-	char real_loop_dev[PATH_MAX];
-
-	if (!realpath(loop_dev, real_loop_dev))
-		return -errno;
-	snprintf(p, PATH_MAX, "/sys/block/%s/loop/backing_file", strrchr(real_loop_dev, '/'));
-	if (!(f = fopen(p, "r"))) {
-		if (errno == ENOENT)
-			/*
-			 * It's possibly a partitioned loop device, which is
-			 * resolvable with loopdev API.
-			 */
-			return resolve_loop_device_with_loopdev(loop_dev, loop_file);
-		return -errno;
-	}
-
-	snprintf(fmt, 20, "%%%i[^\n]", max_len - 1);
-	ret = fscanf(f, fmt, loop_file);
-	fclose(f);
-	if (ret == EOF)
-		return -errno;
-
-	return 0;
-}
-
 /*
  * Checks whether a and b are identical or device
  * files associated with the same block device
  */
-static int is_same_blk_file(const char* a, const char* b)
+int is_same_blk_file(const char* a, const char* b)
 {
 	struct stat st_buf_a, st_buf_b;
 	char real_a[PATH_MAX];
@@ -224,55 +148,6 @@ static int is_same_blk_file(const char* a, const char* b)
 	return 0;
 }
 
-/*
- * Checks if a and b are identical or device files associated with the same
- * block device or if one file is a loop device that uses the other file.
- */
-int is_same_loop_file(const char *a, const char *b)
-{
-	char res_a[PATH_MAX];
-	char res_b[PATH_MAX];
-	const char* final_a = NULL;
-	const char* final_b = NULL;
-	int ret;
-
-	/* Resolve a if it is a loop device */
-	if ((ret = is_loop_device(a)) < 0) {
-		if (ret == -ENOENT)
-			return 0;
-		return ret;
-	} else if (ret) {
-		ret = resolve_loop_device(a, res_a, sizeof(res_a));
-		if (ret < 0) {
-			if (errno != EPERM)
-				return ret;
-		} else {
-			final_a = res_a;
-		}
-	} else {
-		final_a = a;
-	}
-
-	/* Resolve b if it is a loop device */
-	if ((ret = is_loop_device(b)) < 0) {
-		if (ret == -ENOENT)
-			return 0;
-		return ret;
-	} else if (ret) {
-		ret = resolve_loop_device(b, res_b, sizeof(res_b));
-		if (ret < 0) {
-			if (errno != EPERM)
-				return ret;
-		} else {
-			final_b = res_b;
-		}
-	} else {
-		final_b = b;
-	}
-
-	return is_same_blk_file(final_a, final_b);
-}
-
 /* Checks if a file exists and is a block or regular file*/
 int path_is_reg_or_block_device(const char *filename)
 {
diff --git a/common/path-utils.h b/common/path-utils.h
index 558fa21adf..6efcd81cf8 100644
--- a/common/path-utils.h
+++ b/common/path-utils.h
@@ -31,7 +31,7 @@ int path_is_a_mount_point(const char *file);
 int path_exists(const char *file);
 int path_is_reg_file(const char *path);
 int path_is_dir(const char *path);
-int is_same_loop_file(const char *a, const char *b);
+int is_same_blk_file(const char* a, const char* b);
 int path_is_reg_or_block_device(const char *filename);
 int path_is_in_dir(const char *parent, const char *path);
 char *path_basename(char *path);