]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lsblk: print all device mountpoints
authorKarel Zak <kzak@redhat.com>
Fri, 8 Jan 2021 12:20:50 +0000 (13:20 +0100)
committerKarel Zak <kzak@redhat.com>
Fri, 8 Jan 2021 12:20:50 +0000 (13:20 +0100)
* add libmount FS to struct lsblk_device

* add new column MOUNTPOINTS (pl.) with multi-line cells to display
  all mountpoints relevant for the device

* the old MOUNTPOINT is backwardly compatible and it (usually) displays the
  last device mountpoint from /proc/self/mountinfo

For example btrfs with more subvolumes:

 $ lsblk -o+MOUNTPOINTS /dev/sdc1
 NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT MOUNTPOINTS
 sdc1   8:33   0  50M  0 part /mnt/test  /mnt/A
                                         /mnt/test
                                         /mnt/B

Note, in this case MOUNTPOINT displays mount point where is mounted
root of the filesystem.

Signed-off-by: Karel Zak <kzak@redhat.com>
misc-utils/lsblk-devtree.c
misc-utils/lsblk-mnt.c
misc-utils/lsblk.8
misc-utils/lsblk.c
misc-utils/lsblk.h

index 7f39313cd14ea84d47461de2d8cc773587774029..ce9d3e84f7713cd3e0a84867f6c6845416cd3b01 100644 (file)
@@ -106,12 +106,12 @@ void lsblk_unref_device(struct lsblk_device *dev)
 
                device_remove_dependences(dev);
                lsblk_device_free_properties(dev->properties);
+               lsblk_device_free_filesystems(dev);
 
                lsblk_unref_device(dev->wholedisk);
 
                free(dev->dm_name);
                free(dev->filename);
-               free(dev->mountpoint);
                free(dev->dedupkey);
 
                ul_unref_path(dev->sysfs);
index c034e34bbaec886f9f83fb0aa4535c6f0c2cae88..bd015dc5a2de355da157a2fdb946f29047201829 100644 (file)
@@ -3,8 +3,6 @@
 #include "xalloc.h"
 #include "nls.h"
 
-#include <libmount.h>
-
 #include "lsblk.h"
 
 static struct libmnt_table *mtab, *swaps;
@@ -18,8 +16,10 @@ static int table_parser_errcb(struct libmnt_table *tb __attribute__((__unused__)
        return 1;
 }
 
-static int is_active_swap(const char *filename)
+static struct libmnt_fs *get_active_swap(const char *filename)
 {
+       assert(filename);
+
        if (!swaps) {
                swaps = mnt_new_table();
                if (!swaps)
@@ -39,19 +39,46 @@ static int is_active_swap(const char *filename)
                }
        }
 
-       return mnt_table_find_srcpath(swaps, filename, MNT_ITER_BACKWARD) != NULL;
+       return mnt_table_find_srcpath(swaps, filename, MNT_ITER_BACKWARD);
+}
+
+void lsblk_device_free_filesystems(struct lsblk_device *dev)
+{
+       if (!dev)
+               return;
+
+       free(dev->fss);
+
+       dev->fss = NULL;
+       dev->nfss = 0;
+       dev->is_mounted = 0;
+       dev->is_swap = 0;
+}
+
+static void add_filesystem(struct lsblk_device *dev, struct libmnt_fs *fs)
+{
+       assert(dev);
+       assert(fs);
+
+       dev->fss = xrealloc(dev->fss, dev->nfss + 1 * sizeof(struct libmnt_fs *));
+       dev->fss[dev->nfss] = fs;
+       dev->nfss++;
+       dev->is_mounted = 1;
 }
 
-char *lsblk_device_get_mountpoint(struct lsblk_device *dev)
+struct libmnt_fs **lsblk_device_get_filesystems(struct lsblk_device *dev, size_t *n)
 {
        struct libmnt_fs *fs;
-       const char *fsroot;
+       struct libmnt_iter *itr = NULL;
+       dev_t devno;
 
        assert(dev);
        assert(dev->filename);
 
-       if (dev->is_mounted || dev->is_swap)
-               return dev->mountpoint;
+       if (dev->is_mounted)
+               goto done;
+
+       lsblk_device_free_filesystems(dev);     /* reset */
 
        if (!mtab) {
                mtab = mnt_new_table();
@@ -72,46 +99,72 @@ char *lsblk_device_get_mountpoint(struct lsblk_device *dev)
                }
        }
 
-       /* Note that maj:min in /proc/self/mountinfo does not have to match with
-        * devno as returned by stat(), so we have to try devname too
+       devno = makedev(dev->maj, dev->min);
+
+       /* All mounpoint where is used devno or device name
+        */
+       itr = mnt_new_iter(MNT_ITER_BACKWARD);
+       while (mnt_table_next_fs(mtab, itr, &fs) == 0) {
+               if (mnt_fs_get_devno(fs) != devno &&
+                   !mnt_fs_streq_srcpath(fs, dev->filename))
+                       continue;
+               add_filesystem(dev, fs);
+       }
+
+       /* Try mnt_table_find_srcpath() which also cannonicalize patchs, etc.
         */
-       fs = mnt_table_find_devno(mtab, makedev(dev->maj, dev->min), MNT_ITER_BACKWARD);
-       if (!fs)
-               fs = mnt_table_find_srcpath(mtab, dev->filename, MNT_ITER_BACKWARD);
-       if (!fs) {
-               if (is_active_swap(dev->filename)) {
-                       dev->mountpoint = xstrdup("[SWAP]");
-                       dev->is_swap = 1;
-               } else
-                       dev->mountpoint = NULL;
-
-               return dev->mountpoint;
+       if (!dev->nfss) {
+               fs = get_active_swap(dev->filename);
+               if (!fs) {
+                       fs = mnt_table_find_srcpath(mtab, dev->filename, MNT_ITER_BACKWARD);
+                       if (fs)
+                               dev->is_swap = 1;
+               }
+               if (fs)
+                       add_filesystem(dev, fs);
        }
 
-       /* found */
-       fsroot = mnt_fs_get_root(fs);
-       if (fsroot && strcmp(fsroot, "/") != 0) {
-               /* hmm.. we found bind mount or btrfs subvolume, let's try to
-                * get real FS root mountpoint */
-               struct libmnt_fs *rfs;
-               struct libmnt_iter *itr = mnt_new_iter(MNT_ITER_BACKWARD);
-
-               mnt_table_set_iter(mtab, itr, fs);
-               while (mnt_table_next_fs(mtab, itr, &rfs) == 0) {
-                       fsroot = mnt_fs_get_root(rfs);
-                       if ((!fsroot || strcmp(fsroot, "/") == 0)
-                           && mnt_fs_match_source(rfs, dev->filename, mntcache)) {
-                               fs = rfs;
+done:
+       mnt_free_iter(itr);
+       if (n)
+               *n = dev->nfss;
+       return dev->fss;
+}
+
+/* Returns mounpoint where the device is mounted. If the device is used for
+ * more filesystems (subvolumes, ...) than returns the "best" one.
+ */
+const char *lsblk_device_get_mountpoint(struct lsblk_device *dev)
+{
+       struct libmnt_fs *fs = NULL;
+       const char *root;
+
+       lsblk_device_get_filesystems(dev, NULL);
+       if (!dev->nfss)
+               return NULL;
+
+       /* lsblk_device_get_filesystems() scans mountinfo/swaps in backward
+        * order. It means the first in fss[] is the last mounted FS. Let's
+        * keep it as default */
+       fs = dev->fss[0];
+       root = mnt_fs_get_root(fs);
+
+       if (root && strcmp(root, "/") != 0) {
+               /* FS is subvolume (or subdirectory bind-mount). Try to get
+                * FS with "/" root */
+               size_t i;
+
+               for (i = 1; i < dev->nfss; i++) {
+                       root = mnt_fs_get_root(dev->fss[i]);
+                       if (!root || strcmp(root, "/") == 0) {
+                               fs = dev->fss[i];
                                break;
                        }
                }
-               mnt_free_iter(itr);
        }
-
-       DBG(DEV, ul_debugobj(dev, "mountpoint: %s", mnt_fs_get_target(fs)));
-       dev->mountpoint = xstrdup(mnt_fs_get_target(fs));
-       dev->is_mounted = 1;
-       return dev->mountpoint;
+       if (mnt_fs_is_swaparea(fs))
+               return "[SWAP]";
+       return mnt_fs_get_target(fs);
 }
 
 void lsblk_mnt_init(void)
index f59e1d8b6a61ac3d70220ee112175be32f5336db..e9bcbfccf5aaaf6174534ef17f975dd80ee2229b 100644 (file)
@@ -46,6 +46,15 @@ case it is recommended to use
 before
 .B lsblk
 to synchronize with udev.
+.PP
+The relationship between block devices and filesystems is not always one-to-one.
+The filesystem may use more block devices, or the same filesystem may be accessible
+by more paths.  This is the reason why
+.B lsblk
+provides MOUNTPOINT and MOUNTPOINTS (pl.) columns.  The column MOUNTPOINT displays
+only one mount point (usually the last mounted instance of the filesystem), and
+the column MOUNTPOINTS displays by multi-line cell all mount points associated
+with the device.
 .SH OPTIONS
 .TP
 .BR \-a , " \-\-all"
index 210a36633129d89812274f28bdeadb57ec236c40..d41743794302f766c99650ea3f961803477477d3 100644 (file)
@@ -51,6 +51,7 @@
 #include "optutils.h"
 #include "fileutils.h"
 #include "loopdev.h"
+#include "buffer.h"
 
 #include "lsblk.h"
 
@@ -75,6 +76,7 @@ enum {
        COL_FSUSEPERC,
        COL_FSVERSION,
        COL_TARGET,
+       COL_TARGETS,
        COL_LABEL,
        COL_UUID,
        COL_PTUUID,
@@ -164,7 +166,9 @@ static struct colinfo infos[] = {
        [COL_FSUSEPERC] = { "FSUSE%", 3, SCOLS_FL_RIGHT, N_("filesystem use percentage") },
        [COL_FSVERSION] = { "FSVER", 0.1, SCOLS_FL_TRUNC, N_("filesystem version") },
 
-       [COL_TARGET] = { "MOUNTPOINT", 0.10, SCOLS_FL_TRUNC, N_("where the device is mounted") },
+       [COL_TARGET]    = { "MOUNTPOINT",  0.10, SCOLS_FL_TRUNC, N_("where the device is mounted") },
+       [COL_TARGETS]   = { "MOUNTPOINTS", 0.10, SCOLS_FL_WRAP,  N_("all locations where device is mounted") },
+
        [COL_LABEL]  = { "LABEL",   0.1, 0, N_("filesystem LABEL") },
        [COL_UUID]   = { "UUID",    36,  0, N_("filesystem UUID") },
 
@@ -620,10 +624,9 @@ static char *get_vfs_attribute(struct lsblk_device *dev, int id)
 {
        char *sizestr;
        uint64_t vfs_attr = 0;
-       char *mnt;
 
        if (!dev->fsstat.f_blocks) {
-               mnt = lsblk_device_get_mountpoint(dev);
+               const char *mnt = lsblk_device_get_mountpoint(dev);
                if (!mnt || dev->is_swap)
                        return NULL;
                if (statvfs(mnt, &dev->fsstat) != 0)
@@ -799,11 +802,27 @@ static char *device_get_data(
                break;
        case COL_TARGET:
        {
-               char *s = lsblk_device_get_mountpoint(dev);
-               if (s)
-                       str = xstrdup(s);
-               else
-                       str = NULL;
+               const char *p = lsblk_device_get_mountpoint(dev);
+               if (p)
+                       str = xstrdup(p);
+               break;
+       }
+       case COL_TARGETS:
+       {
+               size_t i, n = 0;
+               struct ul_buffer buf = UL_INIT_BUFFER;
+               struct libmnt_fs **fss = lsblk_device_get_filesystems(dev, &n);
+
+               for (i = 0; i < n; i++) {
+                       struct libmnt_fs *fs = fss[i];
+                       if (mnt_fs_is_swaparea(fs))
+                               ul_buffer_append_string(&buf, "[SWAP]");
+                       else
+                               ul_buffer_append_string(&buf, mnt_fs_get_target(fs));
+                       if (i + 1 < n)
+                               ul_buffer_append_data(&buf, "\n", 1);
+               }
+               str = ul_buffer_get_data(&buf);
                break;
        }
        case COL_LABEL:
@@ -2182,6 +2201,15 @@ int main(int argc, char *argv[])
                                ci->type == COLTYPE_SORTNUM ? cmp_u64_cells : scols_cmpstr_cells,
                                NULL);
                }
+               /* multi-line cells (now used for MOUNTPOINTS) */
+               if (fl & SCOLS_FL_WRAP) {
+                       scols_column_set_wrapfunc(cl,
+                                               scols_wrapnl_chunksize,
+                                               scols_wrapnl_nextchunk,
+                                               NULL);
+                       scols_column_set_safechars(cl, "\n");
+               }
+
                if (lsblk->flags & LSBLK_JSON) {
                        switch (ci->type) {
                        case COLTYPE_SIZE:
@@ -2189,13 +2217,16 @@ int main(int argc, char *argv[])
                                        break;
                                /* fallthrough */
                        case COLTYPE_NUM:
-                                scols_column_set_json_type(cl, SCOLS_JSON_NUMBER);
+                               scols_column_set_json_type(cl, SCOLS_JSON_NUMBER);
                                break;
                        case COLTYPE_BOOL:
                                scols_column_set_json_type(cl, SCOLS_JSON_BOOLEAN);
                                break;
                        default:
-                               scols_column_set_json_type(cl, SCOLS_JSON_STRING);
+                               if (fl & SCOLS_FL_WRAP)
+                                       scols_column_set_json_type(cl, SCOLS_JSON_ARRAY_STRING);
+                               else
+                                       scols_column_set_json_type(cl, SCOLS_JSON_STRING);
                                break;
                        }
                }
index 17708b4d51d735bc7e301ab81f2911d80ad568eb..61640e825774f1a86d0bfb3c647afc5def3a869d 100644 (file)
@@ -12,6 +12,7 @@
 #include <sys/statvfs.h>
 
 #include <libsmartcols.h>
+#include <libmount.h>
 
 #include "c.h"
 #include "list.h"
@@ -33,7 +34,6 @@ UL_DEBUG_DECLARE_MASK(lsblk);
 
 struct lsblk {
        struct libscols_table *table;   /* output table */
-
        struct libscols_column *sort_col;/* sort output by this column */
 
        int sort_id;                    /* id of the sort column */
@@ -117,7 +117,9 @@ struct lsblk_device {
 
        struct path_cxt *sysfs;
 
-       char *mountpoint;       /* device mountpoint */
+       struct libmnt_fs **fss; /* filesystems attached to the device */
+       size_t nfss;            /* number of items in fss[] */
+
        struct statvfs fsstat;  /* statvfs() result */
 
        int npartitions;        /* # of partitions this device has */
@@ -207,7 +209,9 @@ struct lsblk_iter {
 extern void lsblk_mnt_init(void);
 extern void lsblk_mnt_deinit(void);
 
-extern char *lsblk_device_get_mountpoint(struct lsblk_device *dev);
+extern void lsblk_device_free_filesystems(struct lsblk_device *dev);
+extern const char *lsblk_device_get_mountpoint(struct lsblk_device *dev);
+extern struct libmnt_fs **lsblk_device_get_filesystems(struct lsblk_device *dev, size_t *n);
 
 /* lsblk-properties.c */
 extern void lsblk_device_free_properties(struct lsblk_devprop *p);