Skip to content

Commit

Permalink
btrfs-progs: remove loopback device resolution
Browse files Browse the repository at this point in the history
[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 <[email protected]>
  • Loading branch information
adam900710 committed Jan 16, 2025
1 parent c5e723b commit 6094201
Show file tree
Hide file tree
Showing 3 changed files with 5 additions and 131 deletions.
7 changes: 3 additions & 4 deletions common/open-utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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;
}

Expand Down Expand Up @@ -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)
Expand Down
127 changes: 1 addition & 126 deletions common/path-utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -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)
{
Expand Down
2 changes: 1 addition & 1 deletion common/path-utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 6094201

Please sign in to comment.