From: Junxiao Bi Date: Wed, 25 Oct 2023 20:36:03 +0000 (-0700) Subject: loopdev: report lost loop devices X-Git-Tag: v2.40-rc1~170^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a6ca0456cc6d704a786f6b66d8bb2d89ff18eba7;p=thirdparty%2Futil-linux.git loopdev: report lost loop devices If a /dev/loopX is lost because someone might have removed it by mistake, future losetup operations on that loop device will fail and losetup --all and --list will not report the devices (although kernel still uses it). Since /sysfs still have the loop device intact, detect that and report it. Changes: * --list and --all add "(lost)" after device node path # losetup NAME SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE DIO LOG-SEC /dev/loop0 (lost) 0 0 0 0 /root/l0.img 0 512 # losetup -a /dev/loop0 (lost): []: (/root/l0.img) * use /sys to get devno rather than stat() * report warning after failed operation # losetup -d /dev/loop0 losetup: /dev/loop0: detach failed: No such file or directory losetup: device node /dev/loop0 (7:0) is lost. You may use mknod(1) to recover it. * fix copy & past bugs in some API comments Co-Author: Karel Zak Signed-off-by: Junxiao Bi Signed-off-by: Karel Zak --- diff --git a/include/loopdev.h b/include/loopdev.h index 0ec8aeeab1..d10bf7f376 100644 --- a/include/loopdev.h +++ b/include/loopdev.h @@ -112,6 +112,7 @@ struct loopdev_cxt { char device[128]; /* device path (e.g. /dev/loop) */ char *filename; /* backing file for loopcxt_set_... */ int fd; /* open(/dev/looo) */ + dev_t devno; /* loop device devno from /sys */ mode_t mode; /* fd mode O_{RDONLY,RDWR} */ uint64_t blocksize; /* used by loopcxt_setup_device() */ @@ -120,6 +121,7 @@ struct loopdev_cxt { unsigned int extra_check:1; /* unusual stuff for iterator */ unsigned int info_failed:1; /* LOOP_GET_STATUS ioctl failed */ unsigned int control_ok:1; /* /dev/loop-control success */ + unsigned int is_lost:1; /* device in /sys, but missing in /dev */ struct path_cxt *sysfs; /* pointer to /sys/dev/block// */ struct loop_config config; /* for GET/SET ioctl */ @@ -149,7 +151,9 @@ extern int is_loopdev(const char *device); extern int loopdev_is_autoclear(const char *device); extern char *loopdev_get_backing_file(const char *device); +extern dev_t loopcxt_get_devno(struct loopdev_cxt *lc); extern int loopdev_has_backing_file(const char *device); +extern int loopcxt_is_lost(struct loopdev_cxt *lc); extern int loopdev_is_used(const char *device, const char *filename, uint64_t offset, uint64_t sizelimit, int flags); extern char *loopdev_find_by_backing_file(const char *filename, diff --git a/lib/loopdev.c b/lib/loopdev.c index dae499f256..c72fb2c404 100644 --- a/lib/loopdev.c +++ b/lib/loopdev.c @@ -116,6 +116,8 @@ int loopcxt_set_device(struct loopdev_cxt *lc, const char *device) DBG(CXT, ul_debugobj(lc, "closing old open fd")); } lc->fd = -1; + lc->is_lost = 0; + lc->devno = 0; lc->mode = O_RDONLY; lc->blocksize = 0; lc->has_info = 0; @@ -153,6 +155,28 @@ int loopcxt_has_device(struct loopdev_cxt *lc) return lc && *lc->device; } +dev_t loopcxt_get_devno(struct loopdev_cxt *lc) +{ + if (!lc || !loopcxt_has_device(lc)) + return 0; + if (!lc->devno) + lc->devno = sysfs_devname_to_devno(lc->device); + return lc->devno; +} + +int loopcxt_is_lost(struct loopdev_cxt *lc) +{ + if (!lc || !loopcxt_has_device(lc)) + return 0; + if (lc->is_lost) + return 1; + + lc->is_lost = access(lc->device, F_OK) != 0 + && loopcxt_get_devno(lc) != 0; + + return lc->is_lost; +} + /* * @lc: context * @flags: LOOPDEV_FL_* flags @@ -265,7 +289,7 @@ static struct path_cxt *loopcxt_get_sysfs(struct loopdev_cxt *lc) return NULL; if (!lc->sysfs) { - dev_t devno = sysfs_devname_to_devno(lc->device); + dev_t devno = loopcxt_get_devno(lc); if (!devno) { DBG(CXT, ul_debugobj(lc, "sysfs: failed devname to devno")); return NULL; @@ -416,13 +440,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) @@ -876,7 +893,7 @@ int loopcxt_get_sizelimit(struct loopdev_cxt *lc, uint64_t *size) /* * @lc: context - * @devno: returns encryption type + * @type: returns encryption type * * Cryptoloop is DEPRECATED! * @@ -901,7 +918,6 @@ int loopcxt_get_encrypt_type(struct loopdev_cxt *lc, uint32_t *type) /* * @lc: context - * @devno: returns crypt name * * Cryptoloop is DEPRECATED! * diff --git a/sys-utils/losetup.c b/sys-utils/losetup.c index 454e9a145d..b218ce14d5 100644 --- a/sys-utils/losetup.c +++ b/sys-utils/losetup.c @@ -143,23 +143,26 @@ static int printf_loopdev(struct loopdev_cxt *lc) * Probably non-root user (no permissions to * call LOOP_GET_STATUS ioctls). */ - printf("%s: []: (%s)", - loopcxt_get_device(lc), fname); + printf("%s%s: []: (%s)", + loopcxt_get_device(lc), + loopcxt_is_lost(lc) ? " (lost)" : "", + fname); if (loopcxt_get_offset(lc, &x) == 0 && x) printf(_(", offset %ju"), x); - if (loopcxt_get_sizelimit(lc, &x) == 0 && x) printf(_(", sizelimit %ju"), x); + goto done; } - printf("%s: [%04jd]:%ju (%s)", - loopcxt_get_device(lc), (intmax_t) dev, (uintmax_t) ino, fname); + printf("%s%s: [%04jd]:%ju (%s)", + loopcxt_get_device(lc), + loopcxt_is_lost(lc) ? " (lost)" : "", + (intmax_t) dev, (uintmax_t) ino, fname); if (loopcxt_get_offset(lc, &x) == 0 && x) printf(_(", offset %ju"), x); - if (loopcxt_get_sizelimit(lc, &x) == 0 && x) printf(_(", sizelimit %ju"), x); @@ -210,11 +213,24 @@ static int show_all_loops(struct loopdev_cxt *lc, const char *file, return 0; } +static void warn_lost(struct loopdev_cxt *lc) +{ + dev_t devno = loopcxt_get_devno(lc); + + if (devno <= 0) + return; + + warnx(("device node %s (%u:%u) is lost. You may use mknod(1) to recover it."), + loopcxt_get_device(lc), major(devno), minor(devno)); +} + static int delete_loop(struct loopdev_cxt *lc) { - if (loopcxt_delete_device(lc)) + if (loopcxt_delete_device(lc)) { warn(_("%s: detach failed"), loopcxt_get_device(lc)); - else + if (loopcxt_is_lost(lc)) + warn_lost(lc); + } else return 0; return -1; @@ -234,24 +250,9 @@ static int delete_all_loops(struct loopdev_cxt *lc) return res; } -static dev_t get_device_devno(struct loopdev_cxt *lc, struct stat *st) -{ - if (st->st_rdev) - return st->st_rdev; - - if (loopcxt_get_device(lc) - && stat(loopcxt_get_device(lc), st) == 0 - && S_ISBLK(st->st_mode) - && major(st->st_rdev) == LOOPDEV_MAJOR) - return st->st_rdev; - - return 0; -} - static int set_scols_data(struct loopdev_cxt *lc, struct libscols_line *ln) { size_t i; - struct stat st = { .st_rdev = 0 }; for (i = 0; i < ncolumns; i++) { const char *p = NULL; /* external data */ @@ -262,6 +263,10 @@ static int set_scols_data(struct loopdev_cxt *lc, struct libscols_line *ln) switch(get_column_id(i)) { case COL_NAME: p = loopcxt_get_device(lc); + if (loopcxt_is_lost(lc)) { + xasprintf(&np, "%s (lost)", p); + p = NULL; + } break; case COL_BACK_FILE: np = loopcxt_get_backing_file(lc); @@ -298,20 +303,20 @@ static int set_scols_data(struct loopdev_cxt *lc, struct libscols_line *ln) } case COL_MAJMIN: { - dev_t dev = get_device_devno(lc, &st); + dev_t dev = loopcxt_get_devno(lc); if (dev) xasprintf(&np, raw || json ? "%u:%u" :"%3u:%-3u", major(dev), minor(dev)); break; } case COL_MAJ: { - dev_t dev = get_device_devno(lc, &st); + dev_t dev = loopcxt_get_devno(lc); if (dev) xasprintf(&np, "%u", major(dev)); break; } case COL_MIN: { - dev_t dev = get_device_devno(lc, &st); + dev_t dev = loopcxt_get_devno(lc); if (dev) xasprintf(&np, "%u", minor(dev)); break; @@ -653,7 +658,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 +746,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 +758,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 +886,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 +937,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++; @@ -989,6 +990,12 @@ int main(int argc, char **argv) break; } + if (res && (act == A_SET_CAPACITY + || act == A_SET_DIRECT_IO + || act == A_SET_BLOCKSIZE) + && loopcxt_is_lost(&lc)) + warn_lost(&lc); + loopcxt_deinit(&lc); return res ? EXIT_FAILURE : EXIT_SUCCESS; }