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 <kzak@redhat.com>
This commit is contained in:
Karel Zak 2021-01-08 13:20:50 +01:00
parent 3c7355dd63
commit c6648d2db1
5 changed files with 151 additions and 54 deletions

View 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);

View 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);
}
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)

View 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"

View 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;
}
}

View 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);