diff mbox series

[f2fs-dev,v3] f2fs-tools: fix to check loop device for non-root users

Message ID 20240301071402.159309-1-huangjianan@xiaomi.com (mailing list archive)
State New
Headers show
Series [f2fs-dev,v3] f2fs-tools: fix to check loop device for non-root users | expand

Commit Message

黄佳男 March 1, 2024, 7:14 a.m. UTC
Currently mkfs/fsck gets the following error when executed by
non-root users:

Info: open /dev/loop0 failed errno:13
        Error: Not available on mounted device!

Let's fix it by reading the backing file from sysfs.

Fixes: 14197d546b93 ("f2fs-tools: fix to check loop device")
Signed-off-by: Huang Jianan <huangjianan@xiaomi.com>
---
v3:
- Skip deleted backing file.
 lib/libf2fs.c | 40 +++++++++++++++++++++++++++-------------
 1 file changed, 27 insertions(+), 13 deletions(-)

Comments

Juhyung Park March 1, 2024, 8:39 a.m. UTC | #1
Hi Huang and Chao.

I feel like this special loopback handling alongside Chao's
14197d546b93 on f2fs-tools is just unnecessarily complicating the code
flow.
We're now doing what, lookup to /sys, parse original backing file,
remove trailing newline char, stat()'ing it to make sure it exists?

What if the stat()'ed file is a new file after the original backing
file has been deleted?

Being able to overwrite an active loopback backing file is a semantic
that Linux provides willingly.
O_EXCL only works on block devices and it's a POSIX guarantee that
multiple writers can work on a regular file.

IMHO we should honor that, but if we really want to prevent this akin
to e2fsprogs, we should be using mntent like e2fsprogs.

On Fri, Mar 1, 2024 at 4:15 PM Huang Jianan via Linux-f2fs-devel
<linux-f2fs-devel@lists.sourceforge.net> wrote:
>
> Currently mkfs/fsck gets the following error when executed by
> non-root users:
>
> Info: open /dev/loop0 failed errno:13
>         Error: Not available on mounted device!
>
> Let's fix it by reading the backing file from sysfs.
>
> Fixes: 14197d546b93 ("f2fs-tools: fix to check loop device")
> Signed-off-by: Huang Jianan <huangjianan@xiaomi.com>
> ---
> v3:
> - Skip deleted backing file.
>  lib/libf2fs.c | 40 +++++++++++++++++++++++++++-------------
>  1 file changed, 27 insertions(+), 13 deletions(-)
>
> diff --git a/lib/libf2fs.c b/lib/libf2fs.c
> index d51e485..fad3fd4 100644
> --- a/lib/libf2fs.c
> +++ b/lib/libf2fs.c
> @@ -832,7 +832,7 @@ int f2fs_dev_is_umounted(char *path)
>                 }
>         } else if (S_ISREG(st_buf.st_mode)) {
>                 /* check whether regular is backfile of loop device */
> -#if defined(HAVE_LINUX_LOOP_H) && defined(HAVE_LINUX_MAJOR_H)
> +#if defined(HAVE_LINUX_MAJOR_H)
>                 struct mntent *mnt;
>                 struct stat st_loop;
>                 FILE *f;
> @@ -840,8 +840,9 @@ int f2fs_dev_is_umounted(char *path)
>                 f = setmntent("/proc/mounts", "r");
>
>                 while ((mnt = getmntent(f)) != NULL) {
> -                       struct loop_info64 loopinfo = {0, };
> -                       int loop_fd, err;
> +                       struct stat st_back;
> +                       int sysfs_fd, rc;
> +                       char buf[PATH_MAX + 1];
>
>                         if (mnt->mnt_fsname[0] != '/')
>                                 continue;
> @@ -852,23 +853,36 @@ int f2fs_dev_is_umounted(char *path)
>                         if (major(st_loop.st_rdev) != LOOP_MAJOR)
>                                 continue;
>
> -                       loop_fd = open(mnt->mnt_fsname, O_RDONLY);
> -                       if (loop_fd < 0) {
> +                       snprintf(buf, PATH_MAX,
> +                                "/sys/dev/block/%d:%d/loop/backing_file",
> +                                major(st_loop.st_rdev), minor(st_loop.st_rdev));
> +
> +                       sysfs_fd = open(buf, O_RDONLY);
> +                       if (sysfs_fd < 0) {
>                                 MSG(0, "Info: open %s failed errno:%d\n",
> -                                       mnt->mnt_fsname, errno);
> +                                       buf, errno);
>                                 return -1;
>                         }
>
> -                       err = ioctl(loop_fd, LOOP_GET_STATUS64, &loopinfo);
> -                       close(loop_fd);
> -                       if (err < 0) {
> -                               MSG(0, "\tError: ioctl LOOP_GET_STATUS64 failed errno:%d!\n",
> -                                       errno);
> +                       memset(buf, 0, PATH_MAX + 1);
> +                       rc = read(sysfs_fd, buf, PATH_MAX);
> +                       if (rc < 0) {
> +                               MSG(0, "Info: read %s failed errno:%d\n",
> +                                       buf, errno);
>                                 return -1;
>                         }
>
> -                       if (st_buf.st_dev == loopinfo.lo_device &&
> -                               st_buf.st_ino == loopinfo.lo_inode) {
> +                       /* Remove trailing newline */
> +                       if (rc > 0 && *(buf + rc - 1) == '\n')
> +                               --rc;
> +                       buf[rc] = '\0';
> +
> +                       /* Skip deleted files like "xxx (deleted)" */
> +                       if (stat(buf, &st_back) != 0)
> +                               continue;
> +
> +                       if (st_buf.st_dev == st_back.st_dev &&
> +                               st_buf.st_ino == st_back.st_ino) {
>                                 MSG(0, "\tError: In use by loop device!\n");
>                                 return -EBUSY;
>                         }
> --
> 2.34.1
>
>
>
> _______________________________________________
> Linux-f2fs-devel mailing list
> Linux-f2fs-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
黄佳男 March 1, 2024, 10:32 a.m. UTC | #2
On 2024/3/1 16:39, Juhyung Park wrote:
> [外部邮件] 此邮件来源于小米公司外部,请谨慎处理。若对邮件安全性存疑,请将邮件转发给misec@xiaomi.com进行反馈
>
> Hi Huang and Chao.
>
> I feel like this special loopback handling alongside Chao's
> 14197d546b93 on f2fs-tools is just unnecessarily complicating the code
> flow.
> We're now doing what, lookup to /sys, parse original backing file,
> remove trailing newline char, stat()'ing it to make sure it exists?

Indeed this is not a good approach.

> What if the stat()'ed file is a new file after the original backing
> file has been deleted?
>
> Being able to overwrite an active loopback backing file is a semantic
> that Linux provides willingly.
> O_EXCL only works on block devices and it's a POSIX guarantee that
> multiple writers can work on a regular file.
>
> IMHO we should honor that, but if we really want to prevent this akin
> to e2fsprogs, we should be using mntent like e2fsprogs.

e2fsprogs will continue to check if opening the loop device fails, 
rather than

exiting. This way non-root users can use mkfs/fsck normally, although there

may be overwrite issues.


Thanks,

Jianan

> On Fri, Mar 1, 2024 at 4:15 PM Huang Jianan via Linux-f2fs-devel
> <linux-f2fs-devel@lists.sourceforge.net> wrote:
>> Currently mkfs/fsck gets the following error when executed by
>> non-root users:
>>
>> Info: open /dev/loop0 failed errno:13
>>          Error: Not available on mounted device!
>>
>> Let's fix it by reading the backing file from sysfs.
>>
>> Fixes: 14197d546b93 ("f2fs-tools: fix to check loop device")
>> Signed-off-by: Huang Jianan <huangjianan@xiaomi.com>
>> ---
>> v3:
>> - Skip deleted backing file.
>>   lib/libf2fs.c | 40 +++++++++++++++++++++++++++-------------
>>   1 file changed, 27 insertions(+), 13 deletions(-)
>>
>> diff --git a/lib/libf2fs.c b/lib/libf2fs.c
>> index d51e485..fad3fd4 100644
>> --- a/lib/libf2fs.c
>> +++ b/lib/libf2fs.c
>> @@ -832,7 +832,7 @@ int f2fs_dev_is_umounted(char *path)
>>                  }
>>          } else if (S_ISREG(st_buf.st_mode)) {
>>                  /* check whether regular is backfile of loop device */
>> -#if defined(HAVE_LINUX_LOOP_H) && defined(HAVE_LINUX_MAJOR_H)
>> +#if defined(HAVE_LINUX_MAJOR_H)
>>                  struct mntent *mnt;
>>                  struct stat st_loop;
>>                  FILE *f;
>> @@ -840,8 +840,9 @@ int f2fs_dev_is_umounted(char *path)
>>                  f = setmntent("/proc/mounts", "r");
>>
>>                  while ((mnt = getmntent(f)) != NULL) {
>> -                       struct loop_info64 loopinfo = {0, };
>> -                       int loop_fd, err;
>> +                       struct stat st_back;
>> +                       int sysfs_fd, rc;
>> +                       char buf[PATH_MAX + 1];
>>
>>                          if (mnt->mnt_fsname[0] != '/')
>>                                  continue;
>> @@ -852,23 +853,36 @@ int f2fs_dev_is_umounted(char *path)
>>                          if (major(st_loop.st_rdev) != LOOP_MAJOR)
>>                                  continue;
>>
>> -                       loop_fd = open(mnt->mnt_fsname, O_RDONLY);
>> -                       if (loop_fd < 0) {
>> +                       snprintf(buf, PATH_MAX,
>> +                                "/sys/dev/block/%d:%d/loop/backing_file",
>> +                                major(st_loop.st_rdev), minor(st_loop.st_rdev));
>> +
>> +                       sysfs_fd = open(buf, O_RDONLY);
>> +                       if (sysfs_fd < 0) {
>>                                  MSG(0, "Info: open %s failed errno:%d\n",
>> -                                       mnt->mnt_fsname, errno);
>> +                                       buf, errno);
>>                                  return -1;
>>                          }
>>
>> -                       err = ioctl(loop_fd, LOOP_GET_STATUS64, &loopinfo);
>> -                       close(loop_fd);
>> -                       if (err < 0) {
>> -                               MSG(0, "\tError: ioctl LOOP_GET_STATUS64 failed errno:%d!\n",
>> -                                       errno);
>> +                       memset(buf, 0, PATH_MAX + 1);
>> +                       rc = read(sysfs_fd, buf, PATH_MAX);
>> +                       if (rc < 0) {
>> +                               MSG(0, "Info: read %s failed errno:%d\n",
>> +                                       buf, errno);
>>                                  return -1;
>>                          }
>>
>> -                       if (st_buf.st_dev == loopinfo.lo_device &&
>> -                               st_buf.st_ino == loopinfo.lo_inode) {
>> +                       /* Remove trailing newline */
>> +                       if (rc > 0 && *(buf + rc - 1) == '\n')
>> +                               --rc;
>> +                       buf[rc] = '\0';
>> +
>> +                       /* Skip deleted files like "xxx (deleted)" */
>> +                       if (stat(buf, &st_back) != 0)
>> +                               continue;
>> +
>> +                       if (st_buf.st_dev == st_back.st_dev &&
>> +                               st_buf.st_ino == st_back.st_ino) {
>>                                  MSG(0, "\tError: In use by loop device!\n");
>>                                  return -EBUSY;
>>                          }
>> --
>> 2.34.1
>>
>>
>>
>> _______________________________________________
>> Linux-f2fs-devel mailing list
>> Linux-f2fs-devel@lists.sourceforge.net
>> https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
Jaegeuk Kim March 5, 2024, 8:51 p.m. UTC | #3
On 03/01, 黄佳男 wrote:
> On 2024/3/1 16:39, Juhyung Park wrote:
> > [外部邮件] 此邮件来源于小米公司外部,请谨慎处理。若对邮件安全性存疑,请将邮件转发给misec@xiaomi.com进行反馈
> >
> > Hi Huang and Chao.
> >
> > I feel like this special loopback handling alongside Chao's
> > 14197d546b93 on f2fs-tools is just unnecessarily complicating the code
> > flow.
> > We're now doing what, lookup to /sys, parse original backing file,
> > remove trailing newline char, stat()'ing it to make sure it exists?
> 
> Indeed this is not a good approach.
> 
> > What if the stat()'ed file is a new file after the original backing
> > file has been deleted?
> >
> > Being able to overwrite an active loopback backing file is a semantic
> > that Linux provides willingly.
> > O_EXCL only works on block devices and it's a POSIX guarantee that
> > multiple writers can work on a regular file.
> >
> > IMHO we should honor that, but if we really want to prevent this akin
> > to e2fsprogs, we should be using mntent like e2fsprogs.
> 
> e2fsprogs will continue to check if opening the loop device fails, 
> rather than
> 
> exiting. This way non-root users can use mkfs/fsck normally, although there
> 
> may be overwrite issues.

I think we need to fix this first and try to find a better way.

https://lore.kernel.org/linux-f2fs-devel/20240305204834.101697-1-jaegeuk@kernel.org/T/#u

> 
> 
> Thanks,
> 
> Jianan
> 
> > On Fri, Mar 1, 2024 at 4:15 PM Huang Jianan via Linux-f2fs-devel
> > <linux-f2fs-devel@lists.sourceforge.net> wrote:
> >> Currently mkfs/fsck gets the following error when executed by
> >> non-root users:
> >>
> >> Info: open /dev/loop0 failed errno:13
> >>          Error: Not available on mounted device!
> >>
> >> Let's fix it by reading the backing file from sysfs.
> >>
> >> Fixes: 14197d546b93 ("f2fs-tools: fix to check loop device")
> >> Signed-off-by: Huang Jianan <huangjianan@xiaomi.com>
> >> ---
> >> v3:
> >> - Skip deleted backing file.
> >>   lib/libf2fs.c | 40 +++++++++++++++++++++++++++-------------
> >>   1 file changed, 27 insertions(+), 13 deletions(-)
> >>
> >> diff --git a/lib/libf2fs.c b/lib/libf2fs.c
> >> index d51e485..fad3fd4 100644
> >> --- a/lib/libf2fs.c
> >> +++ b/lib/libf2fs.c
> >> @@ -832,7 +832,7 @@ int f2fs_dev_is_umounted(char *path)
> >>                  }
> >>          } else if (S_ISREG(st_buf.st_mode)) {
> >>                  /* check whether regular is backfile of loop device */
> >> -#if defined(HAVE_LINUX_LOOP_H) && defined(HAVE_LINUX_MAJOR_H)
> >> +#if defined(HAVE_LINUX_MAJOR_H)
> >>                  struct mntent *mnt;
> >>                  struct stat st_loop;
> >>                  FILE *f;
> >> @@ -840,8 +840,9 @@ int f2fs_dev_is_umounted(char *path)
> >>                  f = setmntent("/proc/mounts", "r");
> >>
> >>                  while ((mnt = getmntent(f)) != NULL) {
> >> -                       struct loop_info64 loopinfo = {0, };
> >> -                       int loop_fd, err;
> >> +                       struct stat st_back;
> >> +                       int sysfs_fd, rc;
> >> +                       char buf[PATH_MAX + 1];
> >>
> >>                          if (mnt->mnt_fsname[0] != '/')
> >>                                  continue;
> >> @@ -852,23 +853,36 @@ int f2fs_dev_is_umounted(char *path)
> >>                          if (major(st_loop.st_rdev) != LOOP_MAJOR)
> >>                                  continue;
> >>
> >> -                       loop_fd = open(mnt->mnt_fsname, O_RDONLY);
> >> -                       if (loop_fd < 0) {
> >> +                       snprintf(buf, PATH_MAX,
> >> +                                "/sys/dev/block/%d:%d/loop/backing_file",
> >> +                                major(st_loop.st_rdev), minor(st_loop.st_rdev));
> >> +
> >> +                       sysfs_fd = open(buf, O_RDONLY);
> >> +                       if (sysfs_fd < 0) {
> >>                                  MSG(0, "Info: open %s failed errno:%d\n",
> >> -                                       mnt->mnt_fsname, errno);
> >> +                                       buf, errno);
> >>                                  return -1;
> >>                          }
> >>
> >> -                       err = ioctl(loop_fd, LOOP_GET_STATUS64, &loopinfo);
> >> -                       close(loop_fd);
> >> -                       if (err < 0) {
> >> -                               MSG(0, "\tError: ioctl LOOP_GET_STATUS64 failed errno:%d!\n",
> >> -                                       errno);
> >> +                       memset(buf, 0, PATH_MAX + 1);
> >> +                       rc = read(sysfs_fd, buf, PATH_MAX);
> >> +                       if (rc < 0) {
> >> +                               MSG(0, "Info: read %s failed errno:%d\n",
> >> +                                       buf, errno);
> >>                                  return -1;
> >>                          }
> >>
> >> -                       if (st_buf.st_dev == loopinfo.lo_device &&
> >> -                               st_buf.st_ino == loopinfo.lo_inode) {
> >> +                       /* Remove trailing newline */
> >> +                       if (rc > 0 && *(buf + rc - 1) == '\n')
> >> +                               --rc;
> >> +                       buf[rc] = '\0';
> >> +
> >> +                       /* Skip deleted files like "xxx (deleted)" */
> >> +                       if (stat(buf, &st_back) != 0)
> >> +                               continue;
> >> +
> >> +                       if (st_buf.st_dev == st_back.st_dev &&
> >> +                               st_buf.st_ino == st_back.st_ino) {
> >>                                  MSG(0, "\tError: In use by loop device!\n");
> >>                                  return -EBUSY;
> >>                          }
> >> --
> >> 2.34.1
> >>
> >>
> >>
> >> _______________________________________________
> >> Linux-f2fs-devel mailing list
> >> Linux-f2fs-devel@lists.sourceforge.net
> >> https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
> 
>
diff mbox series

Patch

diff --git a/lib/libf2fs.c b/lib/libf2fs.c
index d51e485..fad3fd4 100644
--- a/lib/libf2fs.c
+++ b/lib/libf2fs.c
@@ -832,7 +832,7 @@  int f2fs_dev_is_umounted(char *path)
 		}
 	} else if (S_ISREG(st_buf.st_mode)) {
 		/* check whether regular is backfile of loop device */
-#if defined(HAVE_LINUX_LOOP_H) && defined(HAVE_LINUX_MAJOR_H)
+#if defined(HAVE_LINUX_MAJOR_H)
 		struct mntent *mnt;
 		struct stat st_loop;
 		FILE *f;
@@ -840,8 +840,9 @@  int f2fs_dev_is_umounted(char *path)
 		f = setmntent("/proc/mounts", "r");
 
 		while ((mnt = getmntent(f)) != NULL) {
-			struct loop_info64 loopinfo = {0, };
-			int loop_fd, err;
+			struct stat st_back;
+			int sysfs_fd, rc;
+			char buf[PATH_MAX + 1];
 
 			if (mnt->mnt_fsname[0] != '/')
 				continue;
@@ -852,23 +853,36 @@  int f2fs_dev_is_umounted(char *path)
 			if (major(st_loop.st_rdev) != LOOP_MAJOR)
 				continue;
 
-			loop_fd = open(mnt->mnt_fsname, O_RDONLY);
-			if (loop_fd < 0) {
+			snprintf(buf, PATH_MAX,
+				 "/sys/dev/block/%d:%d/loop/backing_file",
+				 major(st_loop.st_rdev), minor(st_loop.st_rdev));
+
+			sysfs_fd = open(buf, O_RDONLY);
+			if (sysfs_fd < 0) {
 				MSG(0, "Info: open %s failed errno:%d\n",
-					mnt->mnt_fsname, errno);
+					buf, errno);
 				return -1;
 			}
 
-			err = ioctl(loop_fd, LOOP_GET_STATUS64, &loopinfo);
-			close(loop_fd);
-			if (err < 0) {
-				MSG(0, "\tError: ioctl LOOP_GET_STATUS64 failed errno:%d!\n",
-					errno);
+			memset(buf, 0, PATH_MAX + 1);
+			rc = read(sysfs_fd, buf, PATH_MAX);
+			if (rc < 0) {
+				MSG(0, "Info: read %s failed errno:%d\n",
+					buf, errno);
 				return -1;
 			}
 
-			if (st_buf.st_dev == loopinfo.lo_device &&
-				st_buf.st_ino == loopinfo.lo_inode) {
+			/* Remove trailing newline */
+			if (rc > 0 && *(buf + rc - 1) == '\n')
+				--rc;
+			buf[rc] = '\0';
+
+			/* Skip deleted files like "xxx (deleted)" */
+			if (stat(buf, &st_back) != 0)
+				continue;
+
+			if (st_buf.st_dev == st_back.st_dev &&
+				st_buf.st_ino == st_back.st_ino) {
 				MSG(0, "\tError: In use by loop device!\n");
 				return -EBUSY;
 			}