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);
#include "xalloc.h"
#include "nls.h"
-#include <libmount.h>
-
#include "lsblk.h"
static struct libmnt_table *mtab, *swaps;
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)
}
}
- 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();
}
}
- /* 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)
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"
#include "optutils.h"
#include "fileutils.h"
#include "loopdev.h"
+#include "buffer.h"
#include "lsblk.h"
COL_FSUSEPERC,
COL_FSVERSION,
COL_TARGET,
+ COL_TARGETS,
COL_LABEL,
COL_UUID,
COL_PTUUID,
[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") },
{
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)
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:
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:
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;
}
}
#include <sys/statvfs.h>
#include <libsmartcols.h>
+#include <libmount.h>
#include "c.h"
#include "list.h"
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 */
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 */
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);