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" Since /sysfs still have the loop device intact, detect that and report detailed log message to guide user to recover the lost loop device file. # ./losetup -a /dev/loop0: [64512]:19133870 (/tmp/test.img) # rm -rf /dev/loop0 # ./losetup -d /dev/loop0 /dev/loop0 is lost, run "mknod /dev/loop0 b 7 0" to recover it. lt-losetup: /dev/loop0: detach failed: No such file or directory Signed-off-by: Junxiao Bi <junxiao.bi@xxxxxxxxxx> --- v2 <- v1: - Reporting lost loop device file instead of using mknode to recreate v1: https://www.spinics.net/lists/util-linux-ng/msg17471.html lib/loopdev.c | 37 ++++++++++++++++++++++++++++++------- sys-utils/losetup.c | 14 +++++--------- 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/lib/loopdev.c b/lib/loopdev.c index dae499f256fa..c8407857972a 100644 --- a/lib/loopdev.c +++ b/lib/loopdev.c @@ -279,6 +279,29 @@ static struct path_cxt *loopcxt_get_sysfs(struct loopdev_cxt *lc) return lc->sysfs; } +/* + * losetup cmds could fail if /dev/loopX is removed by mistake. + * This function will detect that and report a detailed error + * log to help sysadmin recreate the lost loop device file. + */ +static void loopcxt_scan_lost_device_file(struct loopdev_cxt *lc) +{ + static int scanned = 0; + dev_t devno; + + /*only scan sysfs once for losetup cmd*/ + if (!scanned) + scanned = 1; + else + return; + devno = sysfs_devname_to_devno(lc->device); + if (devno <= 0) + return; + + fprintf(stderr, "%s is lost, run \"mknod %s b %d %d\" to recover it.\n", + lc->device, lc->device, major(devno), minor(devno)); +} + static int __loopcxt_get_fd(struct loopdev_cxt *lc, mode_t mode) { int old = -1; @@ -304,6 +327,13 @@ static int __loopcxt_get_fd(struct loopdev_cxt *lc, mode_t mode) mode == O_RDONLY ? "ro" : mode == O_RDWR ? "rw" : "??")); + /* loop device file not exist. */ + if (lc->fd < 0 && errno == ENOENT) { + DBG(CXT, ul_debugobj(lc, "%s doesn't exist.", + lc->device)); + loopcxt_scan_lost_device_file(lc); + errno = ENOENT; + } if (lc->fd < 0 && old >= 0) { /* restore original on error */ lc->fd = old; @@ -416,13 +446,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