* [PATCH] btrfs-progs: remove loopback device resolution
@ 2025-01-16 9:43 Qu Wenruo
2025-01-16 11:42 ` David Sterba
0 siblings, 1 reply; 5+ messages in thread
From: Qu Wenruo @ 2025-01-16 9:43 UTC (permalink / raw)
To: linux-btrfs
[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 8490be4af070..a292177691e7 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 04861d1668cb..3030ddc47f0c 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 558fa21adfa1..6efcd81cf860 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);
--
2.48.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH] btrfs-progs: remove loopback device resolution
2025-01-16 9:43 [PATCH] btrfs-progs: remove loopback device resolution Qu Wenruo
@ 2025-01-16 11:42 ` David Sterba
2025-01-16 20:52 ` Qu Wenruo
0 siblings, 1 reply; 5+ messages in thread
From: David Sterba @ 2025-01-16 11:42 UTC (permalink / raw)
To: Qu Wenruo; +Cc: linux-btrfs
On Thu, Jan 16, 2025 at 08:13:01PM +1030, Qu Wenruo wrote:
> [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.
The loop device resolution had some bugs in the past and was added for a
reason that's not mentioned in the changelogs.
The commits have some details why it's done, they also mention that
partitioned devices are resolved so it's not that there's no support for
that.
0cf3b78f404b01 ("btrfs-progs: Fix partitioned loop devices resolving")
abdb0ced0123d4 ("Btrfs-progs: fix resolving of loop devices")
09559bfe7bcd43 ("multidevice support for check_mounted")
and some fixup commits.
So before removing the code completely I'd like to see if there's a use
case that can be broken, but I don't have anything in particular.
There's only mkfs-tests/006-partitioned-loopdev that's quite simple and
does not try to do any tricks with symlinks or partitions on the
devices.
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] btrfs-progs: remove loopback device resolution
2025-01-16 11:42 ` David Sterba
@ 2025-01-16 20:52 ` Qu Wenruo
2025-01-17 14:01 ` David Sterba
0 siblings, 1 reply; 5+ messages in thread
From: Qu Wenruo @ 2025-01-16 20:52 UTC (permalink / raw)
To: dsterba, Qu Wenruo; +Cc: linux-btrfs
在 2025/1/16 22:12, David Sterba 写道:
> On Thu, Jan 16, 2025 at 08:13:01PM +1030, Qu Wenruo wrote:
>> [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.
>
> The loop device resolution had some bugs in the past and was added for a
> reason that's not mentioned in the changelogs.
>
> The commits have some details why it's done, they also mention that
> partitioned devices are resolved so it's not that there's no support for
> that.
>
> 0cf3b78f404b01 ("btrfs-progs: Fix partitioned loop devices resolving")
> abdb0ced0123d4 ("Btrfs-progs: fix resolving of loop devices")
The problem of those two fixes are pretty simple, they just do not work
in the first place.
resolve_loop_device() is only triggered if is_loop_dev() returns >0 values.
But as I explained, resolve_loop_device() won't return >0 if a
partitioned loopback device is passed.
So why those patches are introduced in the first place?
Thanks,
Qu
> 09559bfe7bcd43 ("multidevice support for check_mounted")
>
> and some fixup commits.
>
> So before removing the code completely I'd like to see if there's a use
> case that can be broken, but I don't have anything in particular.
> There's only mkfs-tests/006-partitioned-loopdev that's quite simple and
> does not try to do any tricks with symlinks or partitions on the
> devices.
>
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] btrfs-progs: remove loopback device resolution
2025-01-16 20:52 ` Qu Wenruo
@ 2025-01-17 14:01 ` David Sterba
2025-01-17 20:35 ` Qu Wenruo
0 siblings, 1 reply; 5+ messages in thread
From: David Sterba @ 2025-01-17 14:01 UTC (permalink / raw)
To: Qu Wenruo; +Cc: dsterba, Qu Wenruo, linux-btrfs
On Fri, Jan 17, 2025 at 07:22:09AM +1030, Qu Wenruo wrote:
>
>
> 在 2025/1/16 22:12, David Sterba 写道:
> > On Thu, Jan 16, 2025 at 08:13:01PM +1030, Qu Wenruo wrote:
> >> [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.
> >
> > The loop device resolution had some bugs in the past and was added for a
> > reason that's not mentioned in the changelogs.
> >
> > The commits have some details why it's done, they also mention that
> > partitioned devices are resolved so it's not that there's no support for
> > that.
> >
> > 0cf3b78f404b01 ("btrfs-progs: Fix partitioned loop devices resolving")
> > abdb0ced0123d4 ("Btrfs-progs: fix resolving of loop devices")
>
> The problem of those two fixes are pretty simple, they just do not work
> in the first place.
>
> resolve_loop_device() is only triggered if is_loop_dev() returns >0 values.
>
> But as I explained, resolve_loop_device() won't return >0 if a
> partitioned loopback device is passed.
>
> So why those patches are introduced in the first place?
I don't know, all I'm asking here is to keep protection against some
accidental use of loop device with mkfs. I looks similar to what we do
with regular block devices, the same device cannot be specified twice.
If the loop device is buggy then it'll be better to fix it then delete
it.
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] btrfs-progs: remove loopback device resolution
2025-01-17 14:01 ` David Sterba
@ 2025-01-17 20:35 ` Qu Wenruo
0 siblings, 0 replies; 5+ messages in thread
From: Qu Wenruo @ 2025-01-17 20:35 UTC (permalink / raw)
To: dsterba; +Cc: Qu Wenruo, linux-btrfs
在 2025/1/18 00:31, David Sterba 写道:
> On Fri, Jan 17, 2025 at 07:22:09AM +1030, Qu Wenruo wrote:
>>
>>
>> 在 2025/1/16 22:12, David Sterba 写道:
>>> On Thu, Jan 16, 2025 at 08:13:01PM +1030, Qu Wenruo wrote:
>>>> [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.
>>>
>>> The loop device resolution had some bugs in the past and was added for a
>>> reason that's not mentioned in the changelogs.
>>>
>>> The commits have some details why it's done, they also mention that
>>> partitioned devices are resolved so it's not that there's no support for
>>> that.
>>>
>>> 0cf3b78f404b01 ("btrfs-progs: Fix partitioned loop devices resolving")
>>> abdb0ced0123d4 ("Btrfs-progs: fix resolving of loop devices")
>>
>> The problem of those two fixes are pretty simple, they just do not work
>> in the first place.
>>
>> resolve_loop_device() is only triggered if is_loop_dev() returns >0 values.
>>
>> But as I explained, resolve_loop_device() won't return >0 if a
>> partitioned loopback device is passed.
>>
>> So why those patches are introduced in the first place?
>
> I don't know, all I'm asking here is to keep protection against some
> accidental use of loop device with mkfs. I looks similar to what we do
> with regular block devices, the same device cannot be specified twice.
That's fine with or without the loopback device detection.
We have duplicated device detection already.
Just try this:
# losetup /dev/loop0 test.img
# mkfs.btrfs -f /dev/loop0 test.img
ERROR: skipping duplicate device /dev/loop0 in the filesystem
ERROR: not enough free space to allocate chunk
This only triggers the duplicated device code, not any loopback related
warning.
> If the loop device is buggy then it'll be better to fix it then delete
> it.
>
As I already mentioned in the commit message, even if I fix
is_loop_device(), then it will cause more problems because we do the
stupid idea of resolving the source file.
Consider this case:
/dev/loop0
|- /dev/loop0p1 BTRFS /mnt/data
|- /dev/loop0p2
Currently we just do not treat /dev/loop0p1/p2 as loop back devices, but
regular block devices.
If we treat them as loop devices (by flawed ideas like checking
"/dev/loop*"), then at check_mounted_where(), /devloop0p1 will be
resolved to the source file, and treated as the whole source file being
mounted at /mnt/data.
Then if we try to mkfs on /dev/loop0p2, we also do the source file
resolution, and find that both p1 and p2 are backed by the same file,
thus unable to mkfs on p2.
Loop device is not only buggy, but flawed in design by day 1.
You need stronger argument to prove the necessary of this feature,
especially no other filesystem do such check at mkfs.
Thanks,
Qu
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2025-01-17 20:35 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-01-16 9:43 [PATCH] btrfs-progs: remove loopback device resolution Qu Wenruo
2025-01-16 11:42 ` David Sterba
2025-01-16 20:52 ` Qu Wenruo
2025-01-17 14:01 ` David Sterba
2025-01-17 20:35 ` Qu Wenruo
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox