From c6648d2db1716978c8344c046ea6c94a68d27238 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 8 Jan 2021 13:20:50 +0100 Subject: [PATCH] lsblk: print all device mountpoints * 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 --- misc-utils/lsblk-devtree.c | 2 +- misc-utils/lsblk-mnt.c | 133 ++++++++++++++++++++++++++----------- misc-utils/lsblk.8 | 9 +++ misc-utils/lsblk.c | 51 +++++++++++--- misc-utils/lsblk.h | 10 ++- 5 files changed, 151 insertions(+), 54 deletions(-) diff --git a/misc-utils/lsblk-devtree.c b/misc-utils/lsblk-devtree.c index 7f39313cd..ce9d3e84f 100644 --- a/misc-utils/lsblk-devtree.c +++ b/misc-utils/lsblk-devtree.c @@ -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); diff --git a/misc-utils/lsblk-mnt.c b/misc-utils/lsblk-mnt.c index c034e34bb..bd015dc5a 100644 --- a/misc-utils/lsblk-mnt.c +++ b/misc-utils/lsblk-mnt.c @@ -3,8 +3,6 @@ #include "xalloc.h" #include "nls.h" -#include - #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); } -char *lsblk_device_get_mountpoint(struct lsblk_device *dev) +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; +} + +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 - */ - 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; + devno = makedev(dev->maj, dev->min); - return dev->mountpoint; + /* 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); } - /* 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); + /* Try mnt_table_find_srcpath() which also cannonicalize patchs, etc. + */ + 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); + } - 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) diff --git a/misc-utils/lsblk.8 b/misc-utils/lsblk.8 index f59e1d8b6..e9bcbfccf 100644 --- a/misc-utils/lsblk.8 +++ b/misc-utils/lsblk.8 @@ -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" diff --git a/misc-utils/lsblk.c b/misc-utils/lsblk.c index 210a36633..d41743794 100644 --- a/misc-utils/lsblk.c +++ b/misc-utils/lsblk.c @@ -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; } } diff --git a/misc-utils/lsblk.h b/misc-utils/lsblk.h index 17708b4d5..61640e825 100644 --- a/misc-utils/lsblk.h +++ b/misc-utils/lsblk.h @@ -12,6 +12,7 @@ #include #include +#include #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);