]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
loopdev: report lost loop devices
authorJunxiao Bi <junxiao.bi@oracle.com>
Wed, 25 Oct 2023 20:36:03 +0000 (13:36 -0700)
committerKarel Zak <kzak@redhat.com>
Wed, 1 Nov 2023 11:39:16 +0000 (12:39 +0100)
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 <kzak@redhat.com>
Signed-off-by: Junxiao Bi <junxiao.bi@oracle.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
include/loopdev.h
lib/loopdev.c
sys-utils/losetup.c

index 0ec8aeeab1d5fa701e72e311081b56b8c3871e4b..d10bf7f376505eb96088ccb4ca75726fd9c28147 100644 (file)
@@ -112,6 +112,7 @@ struct loopdev_cxt {
        char            device[128];    /* device path (e.g. /dev/loop<N>) */
        char            *filename;      /* backing file for loopcxt_set_... */
        int             fd;             /* open(/dev/looo<N>) */
+       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/<maj:min>/ */
        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,
index dae499f256fa07c8b00a9913cd45c9ad81f733b4..c72fb2c404b676ed7cfd0234ebb4b4ad256d2625 100644 (file)
@@ -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!
  *
index 454e9a145d27f602cad6150c1af90ae75835d36c..b218ce14d582a0119703c489e8669ffc88b59205 100644 (file)
@@ -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;
 }