Util-Linux Archive mirror
 help / color / mirror / Atom feed
From: Junxiao Bi <junxiao.bi@oracle.com>
To: util-linux@vger.kernel.org
Cc: junxiao.bi@oracle.com
Subject: [PATCH] loopdev: fix losetup failure with a lost device file
Date: Thu, 12 Oct 2023 14:55:24 -0700	[thread overview]
Message-ID: <20231012215524.44326-1-junxiao.bi@oracle.com> (raw)

If a /dev/loopX is lost because someone might have removed it by mistake,
future losetup operations on that loop device will fail.
For examples, following cmds will fail when loop device file is lost.
- "losetup -d $loop_device"
- "losetup -f $file" or "mount $file $dev"

Users can resurrect the loop device by recreating the device using mknod,
but regular users might not be aware. Since /sysfs still have the loop
device intact, this commit will detect that case by scanning sysfs and
recreate the lost device file.

Signed-off-by: Junxiao Bi <junxiao.bi@oracle.com>
---
 lib/loopdev.c       | 100 ++++++++++++++++++++++++++++++++++++++++----
 sys-utils/losetup.c |  14 +++----
 2 files changed, 97 insertions(+), 17 deletions(-)

diff --git a/lib/loopdev.c b/lib/loopdev.c
index dae499f256fa..4fc617830bae 100644
--- a/lib/loopdev.c
+++ b/lib/loopdev.c
@@ -279,6 +279,87 @@ static struct path_cxt *loopcxt_get_sysfs(struct loopdev_cxt *lc)
 	return lc->sysfs;
 }
 
+static int loopcxt_get_devid(struct loopdev_cxt *lc)
+{
+	int devid = -1;
+	const char *p, *dev = loopcxt_get_device(lc);
+
+	if (!dev)
+		return -EINVAL;
+
+	p = strrchr(dev, '/');
+	if (!p || (sscanf(p, "/loop%d", &devid) != 1
+				&& sscanf(p, "/%d", &devid) != 1))
+		return -EINVAL;
+
+	return devid;
+}
+
+/* scan sysfs entry for loop device. */
+static int loopcxt_scan_sysfs(struct loopdev_cxt *lc)
+{
+	struct dirent *d;
+	DIR *sysblock;
+	int nr;
+	char name[256];
+
+	if (!loopcxt_sysfs_available(lc))
+		return 0;
+
+	sysblock = opendir(_PATH_SYS_BLOCK);
+	if (!sysblock)
+		return 0;
+
+	nr = loopcxt_get_devid(lc);
+	sprintf(name, "loop%d", nr);
+
+	while ((d = readdir(sysblock))) {
+		if (!strcmp(d->d_name, name))
+			return 1;
+	}
+
+	return 0;
+}
+
+/*
+ * losetup cmds could fail if /dev/loopX is removed by mistake.
+ * This function will try to detect whether that is case,
+ * if so, it will recreate the device file and open it.
+ * Return fd if succeed, otherwise negative error code.
+ */
+static int loopcxt_fix_lost_device_file(struct loopdev_cxt *lc)
+{
+	char path[PATH_MAX];
+	int nr, ret;
+	FILE *fp;
+	unsigned int major, minor;
+
+	if (!loopcxt_scan_sysfs(lc))
+		return -1;
+
+	nr = loopcxt_get_devid(lc);
+	if (nr < 0)
+		return -1;
+	ret = snprintf(path, PATH_MAX, "%s/loop%d/dev",
+			_PATH_SYS_BLOCK, nr);
+	if (ret <= 0 || ret >= PATH_MAX)
+		return -1;
+	fp = fopen(path, "r");
+	if (!fp)
+		return -1;
+	ret = fscanf(fp, "%d:%d", &major, &minor);
+	fclose(fp);
+	if (ret != 2)
+		return -1;
+	sprintf(path, "/dev/loop%d", nr);
+	/* default loop device permission is "brw-rw----" */
+	umask(0003);
+	ret = mknod(path, S_IFBLK|0660, makedev(major, minor));
+	if (ret)
+		return -1;
+	return open(path, lc->mode | O_CLOEXEC);
+}
+
 static int __loopcxt_get_fd(struct loopdev_cxt *lc, mode_t mode)
 {
 	int old = -1;
@@ -303,7 +384,17 @@ static int __loopcxt_get_fd(struct loopdev_cxt *lc, mode_t mode)
 		DBG(CXT, ul_debugobj(lc, "open %s [%s]: %m", lc->device,
 				mode == O_RDONLY ? "ro" :
 			        mode == O_RDWR ? "rw" : "??"));
-
+		/* loop device file not exist. */
+		if (lc->fd < 0 && errno == ENOENT) {
+			lc->fd = loopcxt_fix_lost_device_file(lc);
+			DBG(CXT, ul_debugobj(lc, "recreate %s fd %d",
+						lc->device, lc->fd));
+			/* loop file is not lost but doesn't exist,
+			 * reset errno to have user know.
+			 */
+			if (lc->fd < 0)
+				errno = ENOENT;
+		}
 		if (lc->fd < 0 && old >= 0) {
 			/* restore original on error */
 			lc->fd = old;
@@ -416,13 +507,6 @@ static int loopiter_set_device(struct loopdev_cxt *lc, const char *device)
 	    !(lc->iter.flags & LOOPITER_FL_FREE))
 		return 0;	/* caller does not care about device status */
 
-	if (!is_loopdev(lc->device)) {
-		DBG(ITER, ul_debugobj(&lc->iter, "%s does not exist", lc->device));
-		return -errno;
-	}
-
-	DBG(ITER, ul_debugobj(&lc->iter, "%s exist", lc->device));
-
 	used = loopcxt_get_offset(lc, NULL) == 0;
 
 	if ((lc->iter.flags & LOOPITER_FL_USED) && used)
diff --git a/sys-utils/losetup.c b/sys-utils/losetup.c
index 0ca910ae3347..b45cc2ee8f3c 100644
--- a/sys-utils/losetup.c
+++ b/sys-utils/losetup.c
@@ -653,7 +653,7 @@ static int create_loop(struct loopdev_cxt *lc,
 		}
 
 		/* errors */
-		errpre = hasdev && loopcxt_get_fd(lc) < 0 ?
+		errpre = hasdev && lc->fd < 0 ?
 				 loopcxt_get_device(lc) : file;
 		warn(_("%s: failed to set up loop device"), errpre);
 		break;
@@ -741,8 +741,7 @@ int main(int argc, char **argv)
 			break;
 		case 'c':
 			act = A_SET_CAPACITY;
-			if (!is_loopdev(optarg) ||
-			    loopcxt_set_device(&lc, optarg))
+			if (loopcxt_set_device(&lc, optarg))
 				err(EXIT_FAILURE, _("%s: failed to use device"),
 						optarg);
 			break;
@@ -754,8 +753,7 @@ int main(int argc, char **argv)
 			break;
 		case 'd':
 			act = A_DELETE;
-			if (!is_loopdev(optarg) ||
-			    loopcxt_set_device(&lc, optarg))
+			if (loopcxt_set_device(&lc, optarg))
 				err(EXIT_FAILURE, _("%s: failed to use device"),
 						optarg);
 			break;
@@ -883,8 +881,7 @@ int main(int argc, char **argv)
 		else
 			act = A_SHOW_ONE;
 
-		if (!is_loopdev(argv[optind]) ||
-		    loopcxt_set_device(&lc, argv[optind]))
+		if (loopcxt_set_device(&lc, argv[optind]))
 			err(EXIT_FAILURE, _("%s: failed to use device"),
 					argv[optind]);
 		optind++;
@@ -935,8 +932,7 @@ int main(int argc, char **argv)
 	case A_DELETE:
 		res = delete_loop(&lc);
 		while (optind < argc) {
-			if (!is_loopdev(argv[optind]) ||
-			    loopcxt_set_device(&lc, argv[optind]))
+			if (loopcxt_set_device(&lc, argv[optind]))
 				warn(_("%s: failed to use device"),
 						argv[optind]);
 			optind++;
-- 
2.39.3 (Apple Git-145)


             reply	other threads:[~2023-10-12 21:55 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-10-12 21:55 Junxiao Bi [this message]
2023-10-17 23:06 ` [PATCH] loopdev: fix losetup failure with a lost device file junxiao.bi
2023-10-18  0:57   ` Dragan Simic
2023-10-18  3:28     ` Junxiao Bi
2023-10-18  6:18 ` Thomas Weißschuh
2023-10-18 22:06   ` junxiao.bi
2023-10-18 17:59 ` Karel Zak
2023-10-18 22:08   ` junxiao.bi
2023-10-19  0:13   ` Dragan Simic
  -- strict thread matches above, loose matches on Subject: below --
2023-10-12 21:54 Junxiao Bi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20231012215524.44326-1-junxiao.bi@oracle.com \
    --to=junxiao.bi@oracle.com \
    --cc=util-linux@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).