(root)/
util-linux-2.39/
misc-utils/
findmnt.c
       1  /*
       2   * findmnt(8)
       3   *
       4   * Copyright (C) 2010-2015 Red Hat, Inc. All rights reserved.
       5   * Written by Karel Zak <kzak@redhat.com>
       6   *
       7   * This program is free software; you can redistribute it and/or modify
       8   * it under the terms of the GNU General Public License as published by
       9   * the Free Software Foundation; either version 2 of the License, or
      10   * (at your option) any later version.
      11   *
      12   * This program is distributed in the hope that it would be useful,
      13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15   * GNU General Public License for more details.
      16   *
      17   * You should have received a copy of the GNU General Public License along
      18   * with this program; if not, write to the Free Software Foundation, Inc.,
      19   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      20   */
      21  #include <stdio.h>
      22  #include <stdlib.h>
      23  #include <errno.h>
      24  #include <unistd.h>
      25  #include <getopt.h>
      26  #include <string.h>
      27  #include <termios.h>
      28  #ifdef HAVE_SYS_IOCTL_H
      29  # include <sys/ioctl.h>
      30  #endif
      31  #include <assert.h>
      32  #include <poll.h>
      33  #include <sys/statvfs.h>
      34  #include <sys/types.h>
      35  #ifdef HAVE_LIBUDEV
      36  # include <libudev.h>
      37  #endif
      38  #include <blkid.h>
      39  #include <libmount.h>
      40  #include <libsmartcols.h>
      41  
      42  #include "pathnames.h"
      43  #include "nls.h"
      44  #include "closestream.h"
      45  #include "c.h"
      46  #include "strutils.h"
      47  #include "xalloc.h"
      48  #include "optutils.h"
      49  #include "mangle.h"
      50  #include "buffer.h"
      51  
      52  #include "findmnt.h"
      53  
      54  /* column IDs */
      55  enum {
      56  	COL_ACTION,
      57  	COL_AVAIL,
      58  	COL_FREQ,
      59  	COL_FSROOT,
      60  	COL_FSTYPE,
      61  	COL_FS_OPTIONS,
      62  	COL_ID,
      63  	COL_LABEL,
      64  	COL_MAJMIN,
      65  	COL_OLD_OPTIONS,
      66  	COL_OLD_TARGET,
      67  	COL_OPTIONS,
      68  	COL_OPT_FIELDS,
      69  	COL_PARENT,
      70  	COL_PARTLABEL,
      71  	COL_PARTUUID,
      72  	COL_PASSNO,
      73  	COL_PROPAGATION,
      74  	COL_SIZE,
      75  	COL_SOURCE,
      76  	COL_SOURCES,
      77  	COL_TARGET,
      78  	COL_TID,
      79  	COL_USED,
      80  	COL_USEPERC,
      81  	COL_UUID,
      82  	COL_VFS_OPTIONS
      83  };
      84  
      85  enum {
      86  	TABTYPE_FSTAB = 1,
      87  	TABTYPE_MTAB,
      88  	TABTYPE_KERNEL
      89  };
      90  
      91  /* column names */
      92  struct colinfo {
      93  	const char	*name;		/* header */
      94  	double		whint;		/* width hint (N < 1 is in percent of termwidth) */
      95  	int		flags;		/* libsmartcols flags */
      96  	const char      *help;		/* column description */
      97  	const char	*match;		/* pattern for match_func() */
      98  	void		*match_data;	/* match specific data */
      99  };
     100  
     101  /* columns descriptions (don't use const, this is writable) */
     102  static struct colinfo infos[] = {
     103  	[COL_ACTION]       = { "ACTION",         10, SCOLS_FL_STRICTWIDTH, N_("action detected by --poll") },
     104  	[COL_AVAIL]        = { "AVAIL",           5, SCOLS_FL_RIGHT, N_("filesystem size available") },
     105  	[COL_FREQ]         = { "FREQ",            1, SCOLS_FL_RIGHT, N_("dump(8) period in days [fstab only]") },
     106  	[COL_FSROOT]       = { "FSROOT",       0.25, SCOLS_FL_NOEXTREMES, N_("filesystem root") },
     107  	[COL_FSTYPE]       = { "FSTYPE",       0.10, SCOLS_FL_TRUNC, N_("filesystem type") },
     108  	[COL_FS_OPTIONS]   = { "FS-OPTIONS",   0.10, SCOLS_FL_TRUNC, N_("FS specific mount options") },
     109  	[COL_ID]           = { "ID",              2, SCOLS_FL_RIGHT, N_("mount ID") },
     110  	[COL_LABEL]        = { "LABEL",        0.10, 0, N_("filesystem label") },
     111  	[COL_MAJMIN]       = { "MAJ:MIN",         6, 0, N_("major:minor device number") },
     112  	[COL_OLD_OPTIONS]  = { "OLD-OPTIONS",  0.10, SCOLS_FL_TRUNC, N_("old mount options saved by --poll") },
     113  	[COL_OLD_TARGET]   = { "OLD-TARGET",   0.30, 0, N_("old mountpoint saved by --poll") },
     114  	[COL_OPTIONS]      = { "OPTIONS",      0.10, SCOLS_FL_TRUNC, N_("all mount options") },
     115  	[COL_OPT_FIELDS]   = { "OPT-FIELDS",   0.10, SCOLS_FL_TRUNC, N_("optional mount fields") },
     116  	[COL_PARENT]       = { "PARENT",          2, SCOLS_FL_RIGHT, N_("mount parent ID") },
     117  	[COL_PARTLABEL]    = { "PARTLABEL",    0.10, 0, N_("partition label") },
     118  	[COL_PARTUUID]     = { "PARTUUID",       36, 0, N_("partition UUID") },
     119  	[COL_PASSNO]       = { "PASSNO",          1, SCOLS_FL_RIGHT, N_("pass number on parallel fsck(8) [fstab only]") },
     120  	[COL_PROPAGATION]  = { "PROPAGATION",  0.10, 0, N_("VFS propagation flags") },
     121  	[COL_SIZE]         = { "SIZE",            5, SCOLS_FL_RIGHT, N_("filesystem size") },
     122  	[COL_SOURCES]      = { "SOURCES",      0.25, SCOLS_FL_WRAP, N_("all possible source devices") },
     123  	[COL_SOURCE]       = { "SOURCE",       0.25, SCOLS_FL_NOEXTREMES, N_("source device") },
     124  	[COL_TARGET]       = { "TARGET",       0.30, SCOLS_FL_TREE| SCOLS_FL_NOEXTREMES, N_("mountpoint") },
     125  	[COL_TID]          = { "TID",             4, SCOLS_FL_RIGHT, N_("task ID") },
     126  	[COL_USED]         = { "USED",            5, SCOLS_FL_RIGHT, N_("filesystem size used") },
     127  	[COL_USEPERC]      = { "USE%",            3, SCOLS_FL_RIGHT, N_("filesystem use percentage") },
     128  	[COL_UUID]         = { "UUID",           36, 0, N_("filesystem UUID") },
     129  	[COL_VFS_OPTIONS]  = { "VFS-OPTIONS",  0.20, SCOLS_FL_TRUNC, N_("VFS specific mount options") }
     130  };
     131  
     132  /* columns[] array specifies all currently wanted output column. The columns
     133   * are defined by infos[] array and you can specify (on command line) each
     134   * column twice. That's enough, dynamically allocated array of the columns is
     135   * unnecessary overkill and over-engineering in this case */
     136  static int columns[ARRAY_SIZE(infos) * 2];
     137  static size_t ncolumns;
     138  
     139  static inline size_t err_columns_index(size_t arysz, size_t idx)
     140  {
     141  	if (idx >= arysz)
     142  		errx(EXIT_FAILURE, _("too many columns specified, "
     143  				     "the limit is %zu columns"),
     144  				arysz - 1);
     145  	return idx;
     146  }
     147  
     148  #define add_column(ary, n, id)	\
     149  		((ary)[ err_columns_index(ARRAY_SIZE(ary), (n)) ] = (id))
     150  
     151  /* poll actions (parsed --poll=<list> */
     152  #define FINDMNT_NACTIONS	4		/* mount, umount, move, remount */
     153  static int actions[FINDMNT_NACTIONS];
     154  static int nactions;
     155  
     156  /* global (accessed from findmnt-verify.c too) */
     157  unsigned int flags;
     158  int parse_nerrors;
     159  struct libmnt_cache *cache;
     160  
     161  static blkid_cache blk_cache;
     162  
     163  #ifdef HAVE_LIBUDEV
     164  static struct udev *udev;
     165  #endif
     166  
     167  static int match_func(struct libmnt_fs *fs, void *data __attribute__ ((__unused__)));
     168  
     169  
     170  static int get_column_id(int num)
     171  {
     172  	assert(num >= 0);
     173  	assert((size_t) num < ncolumns);
     174  	assert((size_t) columns[num] < ARRAY_SIZE(infos));
     175  	return columns[num];
     176  }
     177  
     178  static struct colinfo *get_column_info(int num)
     179  {
     180  	return &infos[ get_column_id(num) ];
     181  }
     182  
     183  static const char *column_id_to_name(int id)
     184  {
     185  	assert((size_t) id < ARRAY_SIZE(infos));
     186  	return infos[id].name;
     187  }
     188  
     189  static const char *get_column_name(int num)
     190  {
     191  	return get_column_info(num)->name;
     192  }
     193  
     194  static float get_column_whint(int num)
     195  {
     196  	return get_column_info(num)->whint;
     197  }
     198  
     199  static int get_column_flags(int num)
     200  {
     201  	return get_column_info(num)->flags;
     202  }
     203  
     204  static const char *get_match(int id)
     205  {
     206  	assert((size_t) id < ARRAY_SIZE(infos));
     207  	return infos[id].match;
     208  }
     209  
     210  static void *get_match_data(int id)
     211  {
     212  	assert((size_t) id < ARRAY_SIZE(infos));
     213  	return infos[id].match_data;
     214  }
     215  
     216  static void set_match(int id, const char *match)
     217  {
     218  	assert((size_t) id < ARRAY_SIZE(infos));
     219  	infos[id].match = match;
     220  }
     221  
     222  static void set_match_data(int id, void *data)
     223  {
     224  	assert((size_t) id < ARRAY_SIZE(infos));
     225  	infos[id].match_data = data;
     226  }
     227  
     228  /*
     229   * source match means COL_SOURCE *or* COL_MAJMIN, depends on
     230   * data format.
     231   */
     232  static void set_source_match(const char *data)
     233  {
     234  	int maj, min;
     235  
     236  	if (sscanf(data, "%d:%d", &maj, &min) == 2) {
     237  		dev_t *devno = xmalloc(sizeof(dev_t));
     238  
     239  		*devno = makedev(maj, min);
     240  		set_match(COL_MAJMIN, data);
     241  		set_match_data(COL_MAJMIN, (void *) devno);
     242  		flags |= FL_NOSWAPMATCH;
     243  	} else
     244  		set_match(COL_SOURCE, data);
     245  }
     246  
     247  /*
     248   * Extra functionality for --target <path>. The function mnt_table_find_mountpoint()
     249   * also checks parents (path elements in reverse order) to get mountpoint.
     250   *
     251   * @tb has to be from kernel (so no fstab or so)!
     252   */
     253  static void enable_extra_target_match(struct libmnt_table *tb)
     254  {
     255  	char *cn = NULL;
     256  	const char *tgt = NULL, *mnt = NULL;
     257  	struct libmnt_fs *fs;
     258  
     259  	/*
     260  	 * Check if match pattern is mountpoint, if not use the
     261  	 * real mountpoint.
     262  	 */
     263  	if (flags & FL_NOCACHE)
     264  		tgt = get_match(COL_TARGET);
     265  	else {
     266  		tgt = cn = mnt_resolve_path(get_match(COL_TARGET), cache);
     267  		if (!cn)
     268  			return;
     269  	}
     270  
     271  	fs = mnt_table_find_mountpoint(tb, tgt, MNT_ITER_BACKWARD);
     272  	if (fs)
     273  		mnt = mnt_fs_get_target(fs);
     274  	if (mnt && strcmp(mnt, tgt) != 0)
     275  		set_match(COL_TARGET, xstrdup(mnt));	/* replace the current setting */
     276  
     277  	if (!cache)
     278  		free(cn);
     279  }
     280  
     281  
     282  static int is_tabdiff_column(int id)
     283  {
     284  	assert((size_t) id < ARRAY_SIZE(infos));
     285  
     286  	switch(id) {
     287  	case COL_ACTION:
     288  	case COL_OLD_TARGET:
     289  	case COL_OLD_OPTIONS:
     290  		return 1;
     291  	default:
     292  		break;
     293  	}
     294  	return 0;
     295  }
     296  
     297  /*
     298   * "findmnt" without any filter
     299   */
     300  int is_listall_mode(void)
     301  {
     302  	if ((flags & FL_DF || flags & FL_REAL || flags & FL_PSEUDO) && !(flags & FL_ALL))
     303  		return 0;
     304  
     305  	return (!get_match(COL_SOURCE) &&
     306  		!get_match(COL_TARGET) &&
     307  		!get_match(COL_FSTYPE) &&
     308  		!get_match(COL_OPTIONS) &&
     309  		!get_match(COL_MAJMIN));
     310  }
     311  
     312  /*
     313   * Returns 1 if the @act is in the --poll=<list>
     314   */
     315  static int has_poll_action(int act)
     316  {
     317  	int i;
     318  
     319  	if (!nactions)
     320  		return 1;	/* all actions enabled */
     321  	for (i = 0; i < nactions; i++)
     322  		if (actions[i] == act)
     323  			return 1;
     324  	return 0;
     325  }
     326  
     327  static int poll_action_name_to_id(const char *name, size_t namesz)
     328  {
     329  	int id = -1;
     330  
     331  	if (strncasecmp(name, "move", namesz) == 0 && namesz == 4)
     332  		id = MNT_TABDIFF_MOVE;
     333  	else if (strncasecmp(name, "mount", namesz) == 0 && namesz == 5)
     334  		id = MNT_TABDIFF_MOUNT;
     335  	else if (strncasecmp(name, "umount", namesz) == 0 && namesz == 6)
     336  		id = MNT_TABDIFF_UMOUNT;
     337  	else if (strncasecmp(name, "remount", namesz) == 0 && namesz == 7)
     338  		id = MNT_TABDIFF_REMOUNT;
     339  	else
     340  		warnx(_("unknown action: %s"), name);
     341  
     342  	return id;
     343  }
     344  
     345  /*
     346   * findmnt --first-only <devname|TAG=|mountpoint>
     347   *
     348   * ... it works like "mount <devname|TAG=|mountpoint>"
     349   */
     350  static int is_mount_compatible_mode(void)
     351  {
     352  	if (!get_match(COL_SOURCE))
     353  	       return 0;		/* <devname|TAG=|mountpoint> is required */
     354  	if (get_match(COL_FSTYPE) || get_match(COL_OPTIONS))
     355  		return 0;		/* cannot be restricted by -t or -O */
     356  	if (!(flags & FL_FIRSTONLY))
     357  		return 0;		/* we have to return the first entry only */
     358  
     359  	return 1;			/* ok */
     360  }
     361  
     362  static void disable_columns_truncate(void)
     363  {
     364  	size_t i;
     365  
     366  	for (i = 0; i < ARRAY_SIZE(infos); i++)
     367  		infos[i].flags &= ~SCOLS_FL_TRUNC;
     368  }
     369  
     370  /*
     371   * converts @name to column ID
     372   */
     373  static int column_name_to_id(const char *name, size_t namesz)
     374  {
     375  	size_t i;
     376  
     377  	for (i = 0; i < ARRAY_SIZE(infos); i++) {
     378  		const char *cn = column_id_to_name(i);
     379  
     380  		if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
     381  			return i;
     382  	}
     383  	warnx(_("unknown column: %s"), name);
     384  	return -1;
     385  }
     386  
     387  
     388  #ifdef HAVE_LIBUDEV
     389  static char *get_tag_from_udev(const char *devname, int col)
     390  {
     391  	struct udev_device *dev;
     392  	const char *data = NULL;
     393  	char *res = NULL, *path;
     394  
     395  	if (!udev)
     396  		udev = udev_new();
     397  	if (!udev)
     398  		return NULL;
     399  
     400  	/* libudev don't like /dev/mapper/ symlinks */
     401  	path = realpath(devname, NULL);
     402  	if (path)
     403  		devname = path;
     404  
     405  	if (strncmp(devname, "/dev/", 5) == 0)
     406  		devname += 5;
     407  
     408  	dev = udev_device_new_from_subsystem_sysname(udev, "block", devname);
     409  	free(path);
     410  
     411  	if (!dev)
     412  		return NULL;
     413  
     414  	switch(col) {
     415  	case COL_LABEL:
     416  		data = udev_device_get_property_value(dev, "ID_FS_LABEL_ENC");
     417  		break;
     418  	case COL_UUID:
     419  		data = udev_device_get_property_value(dev, "ID_FS_UUID_ENC");
     420  		break;
     421  	case COL_PARTUUID:
     422  		data = udev_device_get_property_value(dev, "ID_PART_ENTRY_UUID");
     423  		break;
     424  	case COL_PARTLABEL:
     425  		data = udev_device_get_property_value(dev, "ID_PART_ENTRY_NAME");
     426  		break;
     427  	default:
     428  		break;
     429  	}
     430  
     431  	if (data) {
     432  		res = xstrdup(data);
     433  		unhexmangle_string(res);
     434  	}
     435  
     436  	udev_device_unref(dev);
     437  	return res;
     438  }
     439  #endif /* HAVE_LIBUDEV */
     440  
     441  /* Returns LABEL or UUID */
     442  static char *get_tag(struct libmnt_fs *fs, const char *tagname, int col
     443  #ifndef HAVE_LIBUDEV
     444  		__attribute__((__unused__))
     445  #endif
     446  		)
     447  {
     448  	const char *t, *v;
     449  	char *res = NULL;
     450  
     451  	if (!mnt_fs_get_tag(fs, &t, &v) && !strcmp(t, tagname))
     452  		res = xstrdup(v);
     453  	else {
     454  		const char *dev = mnt_fs_get_source(fs);
     455  
     456  		if (dev && !(flags & FL_NOCACHE))
     457  			dev = mnt_resolve_spec(dev, cache);
     458  #ifdef HAVE_LIBUDEV
     459  		if (dev)
     460  			res = get_tag_from_udev(dev, col);
     461  #endif
     462  		if (!res) {
     463  			res = mnt_cache_find_tag_value(cache, dev, tagname);
     464  			if (res && cache)
     465  				/* don't return pointer to cache */
     466  				res = xstrdup(res);
     467  		}
     468  	}
     469  
     470  	return res;
     471  }
     472  
     473  static char *get_vfs_attr(struct libmnt_fs *fs, int sizetype)
     474  {
     475  	struct statvfs buf;
     476  	uint64_t vfs_attr = 0;
     477  	char *sizestr;
     478  
     479  	if (statvfs(mnt_fs_get_target(fs), &buf) != 0)
     480  		return NULL;
     481  
     482  	switch(sizetype) {
     483  	case COL_SIZE:
     484  		vfs_attr = buf.f_frsize * buf.f_blocks;
     485  		break;
     486  	case COL_AVAIL:
     487  		vfs_attr = buf.f_frsize * buf.f_bavail;
     488  		break;
     489  	case COL_USED:
     490  		vfs_attr = buf.f_frsize * (buf.f_blocks - buf.f_bfree);
     491  		break;
     492  	case COL_USEPERC:
     493  		if (buf.f_blocks == 0)
     494  			return xstrdup("-");
     495  
     496  		xasprintf(&sizestr, "%.0f%%",
     497  				(double)(buf.f_blocks - buf.f_bfree) /
     498  				buf.f_blocks * 100);
     499  		return sizestr;
     500  	}
     501  
     502  	if (!vfs_attr)
     503  		sizestr = xstrdup("0");
     504  	else if (flags & FL_BYTES)
     505  		xasprintf(&sizestr, "%ju", vfs_attr);
     506  	else
     507  		sizestr = size_to_human_string(SIZE_SUFFIX_1LETTER, vfs_attr);
     508  
     509  	return sizestr;
     510  }
     511  
     512  /* reads sources from libmount/libblkid
     513   */
     514  static char *get_data_col_sources(struct libmnt_fs *fs, int evaluate)
     515  {
     516  	const char *tag = NULL, *p = NULL;
     517  	int i = 0;
     518  	const char *device = NULL;
     519  	char *val = NULL;
     520  	blkid_dev_iterate iter;
     521  	blkid_dev dev;
     522  	struct ul_buffer buf = UL_INIT_BUFFER;
     523  
     524  	/* get TAG from libmount if avalable (e.g. fstab) */
     525  	if (mnt_fs_get_tag(fs, &tag, &p) == 0) {
     526  
     527  		/* if device is in the form 'UUID=..' or 'LABEL=..' and evaluate==0
     528  		 * then it is ok to do not search for multiple devices
     529  		 */
     530  		if (!evaluate)
     531  			goto nothing;
     532  		val = xstrdup(p);
     533  	}
     534  
     535  	/* or get device name */
     536  	else if (!(device = mnt_fs_get_srcpath(fs))
     537  		 || strncmp(device, "/dev/", 5) != 0)
     538  		goto nothing;
     539  
     540  	if (!blk_cache) {
     541  		if (blkid_get_cache(&blk_cache, NULL) != 0)
     542  			goto nothing;
     543  		blkid_probe_all(blk_cache);
     544  	}
     545  
     546  	/* ask libblkid for the UUID */
     547  	if (!val) {
     548  		assert(device);
     549  
     550  		tag = "UUID";
     551  		val = blkid_get_tag_value(blk_cache, "UUID", device);	/* returns allocated string */
     552  		if (!val)
     553  			goto nothing;
     554  	}
     555  
     556  	assert(val);
     557  	assert(tag);
     558  
     559  	/* scan all devices for the TAG */
     560  	iter = blkid_dev_iterate_begin(blk_cache);
     561  	blkid_dev_set_search(iter, tag, val);
     562  
     563  	while (blkid_dev_next(iter, &dev) == 0) {
     564  		dev = blkid_verify(blk_cache, dev);
     565  		if (!dev)
     566  			continue;
     567  		if (i != 0)
     568  			ul_buffer_append_data(&buf, "\n", 1);
     569  		ul_buffer_append_string(&buf, blkid_dev_devname(dev));
     570  		i++;
     571  	}
     572  	blkid_dev_iterate_end(iter);
     573  	free(val);
     574  
     575  	return ul_buffer_get_data(&buf, NULL, NULL);
     576  
     577  nothing:
     578  	free(val);
     579  	return NULL;
     580  }
     581  
     582  /* reads FS data from libmount
     583   */
     584  static char *get_data(struct libmnt_fs *fs, int num)
     585  {
     586  	char *str = NULL;
     587  	int col_id = get_column_id(num);
     588  
     589  	switch (col_id) {
     590  	case COL_SOURCES:
     591  		/* print all devices with the same tag (LABEL, UUID) */
     592  		str = get_data_col_sources(fs, flags & FL_EVALUATE);
     593  		if (str)
     594  			break;
     595  
     596  		/* fallthrough */
     597  	case COL_SOURCE:
     598  	{
     599  		const char *root = mnt_fs_get_root(fs);
     600  		const char *spec = mnt_fs_get_srcpath(fs);
     601  		char *cn = NULL;
     602  
     603  		if (spec && (flags & FL_CANONICALIZE))
     604  			spec = cn = mnt_resolve_path(spec, cache);
     605  		if (!spec) {
     606  			spec = mnt_fs_get_source(fs);
     607  
     608  			if (spec && (flags & FL_EVALUATE))
     609  				spec = cn = mnt_resolve_spec(spec, cache);
     610  		}
     611  		if (root && spec && !(flags & FL_NOFSROOT) && strcmp(root, "/") != 0)
     612  			xasprintf(&str, "%s[%s]", spec, root);
     613  		else if (spec)
     614  			str = xstrdup(spec);
     615  		if (!cache)
     616  			free(cn);
     617  		break;
     618  	}
     619  
     620  	case COL_TARGET:
     621  		if (mnt_fs_get_target(fs))
     622  			str = xstrdup(mnt_fs_get_target(fs));
     623  		break;
     624  	case COL_FSTYPE:
     625  		if (mnt_fs_get_fstype(fs))
     626  			str = xstrdup(mnt_fs_get_fstype(fs));
     627  		break;
     628  	case COL_OPTIONS:
     629  		if (mnt_fs_get_options(fs))
     630  			str = xstrdup(mnt_fs_get_options(fs));
     631  		break;
     632  	case COL_VFS_OPTIONS:
     633  		if (flags & FL_VFS_ALL)
     634  			str = mnt_fs_get_vfs_options_all(fs);
     635  		else if (mnt_fs_get_vfs_options(fs))
     636  			str = xstrdup(mnt_fs_get_vfs_options(fs));
     637  		break;
     638  	case COL_FS_OPTIONS:
     639  		if (mnt_fs_get_fs_options(fs))
     640  			str = xstrdup(mnt_fs_get_fs_options(fs));
     641  		break;
     642  	case COL_OPT_FIELDS:
     643  		if (mnt_fs_get_optional_fields(fs))
     644  			str = xstrdup(mnt_fs_get_optional_fields(fs));
     645  		break;
     646  	case COL_UUID:
     647  		str = get_tag(fs, "UUID", col_id);
     648  		break;
     649  	case COL_PARTUUID:
     650  		str = get_tag(fs, "PARTUUID", col_id);
     651  		break;
     652  	case COL_LABEL:
     653  		str = get_tag(fs, "LABEL", col_id);
     654  		break;
     655  	case COL_PARTLABEL:
     656  		str = get_tag(fs, "PARTLABEL", col_id);
     657  		break;
     658  
     659  	case COL_MAJMIN:
     660  	{
     661  		dev_t devno = mnt_fs_get_devno(fs);
     662  		if (!devno)
     663  			break;
     664  
     665  		if ((flags & FL_RAW) || (flags & FL_EXPORT) || (flags & FL_JSON))
     666  			xasprintf(&str, "%u:%u", major(devno), minor(devno));
     667  		else
     668  			xasprintf(&str, "%3u:%-3u", major(devno), minor(devno));
     669  		break;
     670  	}
     671  	case COL_SIZE:
     672  	case COL_AVAIL:
     673  	case COL_USED:
     674  	case COL_USEPERC:
     675  		str = get_vfs_attr(fs, col_id);
     676  		break;
     677  	case COL_FSROOT:
     678  		if (mnt_fs_get_root(fs))
     679  			str = xstrdup(mnt_fs_get_root(fs));
     680  		break;
     681  	case COL_TID:
     682  		if (mnt_fs_get_tid(fs))
     683  			xasprintf(&str, "%d", mnt_fs_get_tid(fs));
     684  		break;
     685  	case COL_ID:
     686  		if (mnt_fs_get_id(fs))
     687  			xasprintf(&str, "%d", mnt_fs_get_id(fs));
     688  		break;
     689  	case COL_PARENT:
     690  		if (mnt_fs_get_parent_id(fs))
     691  			xasprintf(&str, "%d", mnt_fs_get_parent_id(fs));
     692  		break;
     693  	case COL_PROPAGATION:
     694  		if (mnt_fs_is_kernel(fs)) {
     695  			unsigned long fl = 0;
     696  			char *n = NULL;
     697  
     698  			if (mnt_fs_get_propagation(fs, &fl) != 0)
     699  				break;
     700  
     701  			n = xstrdup((fl & MS_SHARED) ? "shared" : "private");
     702  
     703  			if (fl & MS_SLAVE) {
     704  				xasprintf(&str, "%s,slave", n);
     705  				free(n);
     706  				n = str;
     707  			}
     708  			if (fl & MS_UNBINDABLE) {
     709  				xasprintf(&str, "%s,unbindable", n);
     710  				free(n);
     711  				n = str;
     712  			}
     713  			str = n;
     714  		}
     715  		break;
     716  	case COL_FREQ:
     717  		if (!mnt_fs_is_kernel(fs))
     718  			xasprintf(&str, "%d", mnt_fs_get_freq(fs));
     719  		break;
     720  	case COL_PASSNO:
     721  		if (!mnt_fs_is_kernel(fs))
     722  			xasprintf(&str, "%d", mnt_fs_get_passno(fs));
     723  		break;
     724  	default:
     725  		break;
     726  	}
     727  	return str;
     728  }
     729  
     730  static char *get_tabdiff_data(struct libmnt_fs *old_fs,
     731  				    struct libmnt_fs *new_fs,
     732  				    int change,
     733  				    int num)
     734  {
     735  	char *str = NULL;
     736  
     737  	switch (get_column_id(num)) {
     738  	case COL_ACTION:
     739  		switch (change) {
     740  		case MNT_TABDIFF_MOUNT:
     741  			str = _("mount");
     742  			break;
     743  		case MNT_TABDIFF_UMOUNT:
     744  			str = _("umount");
     745  			break;
     746  		case MNT_TABDIFF_REMOUNT:
     747  			str = _("remount");
     748  			break;
     749  		case MNT_TABDIFF_MOVE:
     750  			str = _("move");
     751  			break;
     752  		default:
     753  			str = _("unknown");
     754  			break;
     755  		}
     756  		str = xstrdup(str);
     757  		break;
     758  	case COL_OLD_OPTIONS:
     759  		if (old_fs && (change == MNT_TABDIFF_REMOUNT ||
     760  			       change == MNT_TABDIFF_UMOUNT)
     761  		           && mnt_fs_get_options(old_fs))
     762  			str = xstrdup(mnt_fs_get_options(old_fs));
     763  		break;
     764  	case COL_OLD_TARGET:
     765  		if (old_fs && (change == MNT_TABDIFF_MOVE ||
     766  			       change == MNT_TABDIFF_UMOUNT)
     767  			   && mnt_fs_get_target(old_fs))
     768  			str = xstrdup(mnt_fs_get_target(old_fs));
     769  		break;
     770  	default:
     771  		if (new_fs)
     772  			str = get_data(new_fs, num);
     773  		else
     774  			str = get_data(old_fs, num);
     775  		break;
     776  	}
     777  	return str;
     778  }
     779  
     780  /* adds one line to the output @tab */
     781  static struct libscols_line *add_line(struct libscols_table *table, struct libmnt_fs *fs,
     782  					struct libscols_line *parent)
     783  {
     784  	size_t i;
     785  	struct libscols_line *line = scols_table_new_line(table, parent);
     786  
     787  	if (!line)
     788  		err(EXIT_FAILURE, _("failed to allocate output line"));
     789  
     790  	for (i = 0; i < ncolumns; i++) {
     791  		if (scols_line_refer_data(line, i, get_data(fs, i)))
     792  			err(EXIT_FAILURE, _("failed to add output data"));
     793  	}
     794  
     795  	scols_line_set_userdata(line, fs);
     796  	return line;
     797  }
     798  
     799  static struct libscols_line *add_tabdiff_line(struct libscols_table *table, struct libmnt_fs *new_fs,
     800  			struct libmnt_fs *old_fs, int change)
     801  {
     802  	size_t i;
     803  	struct libscols_line *line = scols_table_new_line(table, NULL);
     804  
     805  	if (!line)
     806  		err(EXIT_FAILURE, _("failed to allocate output line"));
     807  
     808  	for (i = 0; i < ncolumns; i++) {
     809  		if (scols_line_refer_data(line, i,
     810  				get_tabdiff_data(old_fs, new_fs, change, i)))
     811  			err(EXIT_FAILURE, _("failed to add output data"));
     812  	}
     813  
     814  	return line;
     815  }
     816  
     817  static int has_line(struct libscols_table *table, struct libmnt_fs *fs)
     818  {
     819  	struct libscols_line *ln;
     820  	struct libscols_iter *itr;
     821  	int rc = 0;
     822  
     823  	itr = scols_new_iter(SCOLS_ITER_FORWARD);
     824  	if (!itr)
     825  		return 0;
     826  
     827  	while (scols_table_next_line(table, itr, &ln) == 0) {
     828  		if ((struct libmnt_fs *) scols_line_get_userdata(ln) == fs) {
     829  			rc = 1;
     830  			break;
     831  		}
     832  	}
     833  
     834  	scols_free_iter(itr);
     835  	return rc;
     836  }
     837  
     838  /* reads filesystems from @tb (libmount) and fillin @table (output table) */
     839  static int create_treenode(struct libscols_table *table, struct libmnt_table *tb,
     840  			   struct libmnt_fs *fs, struct libscols_line *parent_line)
     841  {
     842  	struct libmnt_fs *chld = NULL;
     843  	struct libmnt_iter *itr = NULL;
     844  	struct libscols_line *line;
     845  	int rc = -1, first = 0;
     846  
     847  	if (!fs) {
     848  		/* first call, get root FS */
     849  		if (mnt_table_get_root_fs(tb, &fs))
     850  			goto leave;
     851  		parent_line = NULL;
     852  		first = 1;
     853  
     854  	} else if ((flags & FL_SUBMOUNTS) && has_line(table, fs))
     855  		return 0;
     856  
     857  	itr = mnt_new_iter(MNT_ITER_FORWARD);
     858  	if (!itr)
     859  		goto leave;
     860  
     861  	if ((flags & FL_SUBMOUNTS) || match_func(fs, NULL)) {
     862  		line = add_line(table, fs, parent_line);
     863  		if (!line)
     864  			goto leave;
     865  	} else
     866  		line = parent_line;
     867  
     868  	/*
     869  	 * add all children to the output table
     870  	 */
     871  	while (mnt_table_next_child_fs(tb, itr, fs, &chld) == 0) {
     872  		if (create_treenode(table, tb, chld, line))
     873  			goto leave;
     874  	}
     875  	rc = 0;
     876  
     877  	/* make sure all entries are in the tree */
     878  	if (first && (size_t) mnt_table_get_nents(tb) >
     879  		     (size_t) scols_table_get_nlines(table)) {
     880  		mnt_reset_iter(itr, MNT_ITER_FORWARD);
     881  		fs = NULL;
     882  
     883  		while (mnt_table_next_fs(tb, itr, &fs) == 0) {
     884  			if (!has_line(table, fs) && match_func(fs, NULL))
     885  				create_treenode(table, tb, fs, NULL);
     886  		}
     887  	}
     888  leave:
     889  	mnt_free_iter(itr);
     890  	return rc;
     891  }
     892  
     893  /* error callback */
     894  static int parser_errcb(struct libmnt_table *tb __attribute__ ((__unused__)),
     895  			const char *filename, int line)
     896  {
     897  	warnx(_("%s: parse error at line %d -- ignored"), filename, line);
     898  	++parse_nerrors;
     899  	return 1;
     900  }
     901  
     902  static char **append_tabfile(char **files, int *nfiles, char *filename)
     903  {
     904  	files = xrealloc(files, sizeof(char *) * (*nfiles + 1));
     905  	files[(*nfiles)++] = filename;
     906  	return files;
     907  }
     908  
     909  static char **append_pid_tabfile(char **files, int *nfiles, pid_t pid)
     910  {
     911  	char *path = NULL;
     912  
     913  	xasprintf(&path, "/proc/%d/mountinfo", (int) pid);
     914  	return append_tabfile(files, nfiles, path);
     915  }
     916  
     917  /* calls libmount fstab/mtab/mountinfo parser */
     918  static struct libmnt_table *parse_tabfiles(char **files,
     919  					   int nfiles,
     920  					   int tabtype)
     921  {
     922  	struct libmnt_table *tb;
     923  	int rc = 0;
     924  
     925  	tb = mnt_new_table();
     926  	if (!tb) {
     927  		warn(_("failed to initialize libmount table"));
     928  		return NULL;
     929  	}
     930  	mnt_table_set_parser_errcb(tb, parser_errcb);
     931  
     932  	do {
     933  		/* NULL means that libmount will use default paths */
     934  		const char *path = nfiles ? *files++ : NULL;
     935  
     936  		switch (tabtype) {
     937  		case TABTYPE_FSTAB:
     938  			rc = mnt_table_parse_fstab(tb, path);
     939  			break;
     940  		case TABTYPE_MTAB:
     941  			rc = mnt_table_parse_mtab(tb, path);
     942  			break;
     943  		case TABTYPE_KERNEL:
     944  			if (!path)
     945  				path = access(_PATH_PROC_MOUNTINFO, R_OK) == 0 ?
     946  					      _PATH_PROC_MOUNTINFO :
     947  					      _PATH_PROC_MOUNTS;
     948  
     949  			rc = mnt_table_parse_file(tb, path);
     950  			break;
     951  		}
     952  		if (rc) {
     953  			mnt_unref_table(tb);
     954  			warn(_("can't read %s"), path);
     955  			return NULL;
     956  		}
     957  	} while (--nfiles > 0);
     958  
     959  	return tb;
     960  }
     961  
     962  /*
     963   * Parses mountinfo and calls mnt_cache_set_targets(cache, mtab). Only
     964   * necessary if @tb in main() was read from a non-kernel source.
     965   */
     966  static void cache_set_targets(struct libmnt_cache *tmp)
     967  {
     968  	struct libmnt_table *tb;
     969  	const char *path;
     970  
     971  	tb = mnt_new_table();
     972  	if (!tb)
     973  		return;
     974  
     975  	path = access(_PATH_PROC_MOUNTINFO, R_OK) == 0 ?
     976  		_PATH_PROC_MOUNTINFO :
     977  		_PATH_PROC_MOUNTS;
     978  
     979  	if (mnt_table_parse_file(tb, path) == 0)
     980  		mnt_cache_set_targets(tmp, tb);
     981  
     982  	mnt_unref_table(tb);
     983  }
     984  
     985  /* checks if @tb contains parent->child relations */
     986  static int tab_is_tree(struct libmnt_table *tb)
     987  {
     988  	struct libmnt_fs *fs = NULL;
     989  	struct libmnt_iter *itr;
     990  	int rc = 0;
     991  
     992  	itr = mnt_new_iter(MNT_ITER_BACKWARD);
     993  	if (!itr)
     994  		return 0;
     995  
     996  	rc = (mnt_table_next_fs(tb, itr, &fs) == 0 &&
     997  	      mnt_fs_is_kernel(fs) &&
     998  	      mnt_fs_get_root(fs));
     999  
    1000  	mnt_free_iter(itr);
    1001  	return rc;
    1002  }
    1003  
    1004  /* checks if all fs in @tb are from kernel */
    1005  static int tab_is_kernel(struct libmnt_table *tb)
    1006  {
    1007  	struct libmnt_fs *fs = NULL;
    1008  	struct libmnt_iter *itr;
    1009  
    1010  	itr = mnt_new_iter(MNT_ITER_BACKWARD);
    1011  	if (!itr)
    1012  		return 0;
    1013  
    1014  	while (mnt_table_next_fs(tb, itr, &fs) == 0) {
    1015  		if (!mnt_fs_is_kernel(fs)) {
    1016  			mnt_free_iter(itr);
    1017  			return 0;
    1018  		}
    1019  	}
    1020  
    1021  	mnt_free_iter(itr);
    1022  	return 1;
    1023  }
    1024  
    1025  /* filter function for libmount (mnt_table_find_next_fs()) */
    1026  static int match_func(struct libmnt_fs *fs,
    1027  		      void *data __attribute__ ((__unused__)))
    1028  {
    1029  	int rc = flags & FL_INVERT ? 1 : 0;
    1030  	const char *m;
    1031  	void *md;
    1032  
    1033  	m = get_match(COL_FSTYPE);
    1034  	if (m && !mnt_fs_match_fstype(fs, m))
    1035  		return rc;
    1036  
    1037  	m = get_match(COL_OPTIONS);
    1038  	if (m && !mnt_fs_match_options(fs, m))
    1039  		return rc;
    1040  
    1041  	md = get_match_data(COL_MAJMIN);
    1042  	if (md && mnt_fs_get_devno(fs) != *((dev_t *) md))
    1043  		return rc;
    1044  
    1045  	m = get_match(COL_TARGET);
    1046  	if (m && !mnt_fs_match_target(fs, m, cache))
    1047  		return rc;
    1048  
    1049  	m = get_match(COL_SOURCE);
    1050  	if (m && !mnt_fs_match_source(fs, m, cache))
    1051  		return rc;
    1052  
    1053  	if ((flags & FL_DF) && !(flags & FL_ALL)) {
    1054  		const char *type = mnt_fs_get_fstype(fs);
    1055  
    1056  		if (type && strstr(type, "tmpfs"))	/* tmpfs is wanted */
    1057  			return !rc;
    1058  
    1059  		if (mnt_fs_is_pseudofs(fs))
    1060  			return rc;
    1061  	}
    1062  
    1063  	if ((flags & FL_REAL) && mnt_fs_is_pseudofs(fs))
    1064  	    return rc;
    1065  
    1066  	if ((flags & FL_PSEUDO) && !mnt_fs_is_pseudofs(fs))
    1067  	    return rc;
    1068  
    1069  	if ((flags & FL_SHADOWED)) {
    1070  		struct libmnt_table *tb = NULL;
    1071  
    1072  		mnt_fs_get_table(fs, &tb);
    1073  		if (tb && mnt_table_over_fs(tb, fs, NULL) != 0)
    1074  			return rc;
    1075  	}
    1076  
    1077  	return !rc;
    1078  }
    1079  
    1080  /* iterate over filesystems in @tb */
    1081  struct libmnt_fs *get_next_fs(struct libmnt_table *tb,
    1082  				     struct libmnt_iter *itr)
    1083  {
    1084  	struct libmnt_fs *fs = NULL;
    1085  
    1086  	if (is_listall_mode()) {
    1087  		/*
    1088  		 * Print whole file
    1089  		 */
    1090  		if (mnt_table_next_fs(tb, itr, &fs) != 0)
    1091  			return NULL;
    1092  
    1093  	} else if (is_mount_compatible_mode()) {
    1094  		/*
    1095  		 * Look up for FS in the same way how mount(8) searches in fstab
    1096  		 *
    1097  		 *   findmnt -f <spec>
    1098  		 */
    1099  		fs = mnt_table_find_source(tb, get_match(COL_SOURCE),
    1100  					mnt_iter_get_direction(itr));
    1101  
    1102  		if (!fs && !(flags & FL_NOSWAPMATCH))
    1103  			fs = mnt_table_find_target(tb, get_match(COL_SOURCE),
    1104  					mnt_iter_get_direction(itr));
    1105  	} else {
    1106  		/*
    1107  		 * Look up for all matching entries
    1108  		 *
    1109  		 *    findmnt [-l] <source> <target> [-O <options>] [-t <types>]
    1110  		 *    findmnt [-l] <spec> [-O <options>] [-t <types>]
    1111  		 */
    1112  again:
    1113  		if (mnt_table_find_next_fs(tb, itr, match_func,  NULL, &fs) != 0)
    1114  			fs = NULL;
    1115  
    1116  		if (!fs &&
    1117  		    !(flags & FL_NOSWAPMATCH) &&
    1118  		    !get_match(COL_TARGET) && get_match(COL_SOURCE)) {
    1119  
    1120  			/* swap 'spec' and target. */
    1121  			set_match(COL_TARGET, get_match(COL_SOURCE));
    1122  			set_match(COL_SOURCE, NULL);
    1123  			mnt_reset_iter(itr, -1);
    1124  
    1125  			goto again;
    1126  		}
    1127  	}
    1128  
    1129  	return fs;
    1130  }
    1131  
    1132  /*
    1133   * Filter out unwanted lines for --list output or top level lines for
    1134   * --submounts tree output.
    1135   */
    1136  static int add_matching_lines(struct libmnt_table *tb,
    1137  			      struct libscols_table *table, int direction)
    1138  {
    1139  	struct libmnt_iter *itr;
    1140  	struct libmnt_fs *fs;
    1141  	int nlines = 0, rc = -1;
    1142  
    1143  	itr = mnt_new_iter(direction);
    1144  	if (!itr) {
    1145  		warn(_("failed to initialize libmount iterator"));
    1146  		goto done;
    1147  	}
    1148  
    1149  	while((fs = get_next_fs(tb, itr))) {
    1150  		if ((flags & FL_TREE) || (flags & FL_SUBMOUNTS))
    1151  			rc = create_treenode(table, tb, fs, NULL);
    1152  		else
    1153  			rc = !add_line(table, fs, NULL);
    1154  		if (rc)
    1155  			goto done;
    1156  		nlines++;
    1157  		if (flags & FL_FIRSTONLY)
    1158  			break;
    1159  		flags |= FL_NOSWAPMATCH;
    1160  	}
    1161  
    1162  	if (nlines)
    1163  		rc = 0;
    1164  done:
    1165  	mnt_free_iter(itr);
    1166  	return rc;
    1167  }
    1168  
    1169  static int poll_match(struct libmnt_fs *fs)
    1170  {
    1171  	int rc = match_func(fs, NULL);
    1172  
    1173  	if (rc == 0 && !(flags & FL_NOSWAPMATCH) &&
    1174  	    get_match(COL_SOURCE) && !get_match(COL_TARGET)) {
    1175  		/*
    1176  		 * findmnt --poll /foo
    1177  		 * The '/foo' maybe source as well as target.
    1178  		 */
    1179  		const char *str = get_match(COL_SOURCE);
    1180  
    1181  		set_match(COL_TARGET, str);	/* swap */
    1182  		set_match(COL_SOURCE, NULL);
    1183  
    1184  		rc = match_func(fs, NULL);
    1185  
    1186  		set_match(COL_TARGET, NULL);	/* restore */
    1187  		set_match(COL_SOURCE, str);
    1188  
    1189  	}
    1190  	return rc;
    1191  }
    1192  
    1193  static int poll_table(struct libmnt_table *tb, const char *tabfile,
    1194  		  int timeout, struct libscols_table *table, int direction)
    1195  {
    1196  	FILE *f = NULL;
    1197  	int rc = -1;
    1198  	struct libmnt_iter *itr = NULL;
    1199  	struct libmnt_table *tb_new;
    1200  	struct libmnt_tabdiff *diff = NULL;
    1201  	struct pollfd fds[1];
    1202  
    1203  	tb_new = mnt_new_table();
    1204  	if (!tb_new) {
    1205  		warn(_("failed to initialize libmount table"));
    1206  		goto done;
    1207  	}
    1208  
    1209  	itr = mnt_new_iter(direction);
    1210  	if (!itr) {
    1211  		warn(_("failed to initialize libmount iterator"));
    1212  		goto done;
    1213  	}
    1214  
    1215  	diff = mnt_new_tabdiff();
    1216  	if (!diff) {
    1217  		warn(_("failed to initialize libmount tabdiff"));
    1218  		goto done;
    1219  	}
    1220  
    1221  	/* cache is unnecessary to detect changes */
    1222  	mnt_table_set_cache(tb, NULL);
    1223  	mnt_table_set_cache(tb_new, NULL);
    1224  
    1225  	f = fopen(tabfile, "r");
    1226  	if (!f) {
    1227  		warn(_("cannot open %s"), tabfile);
    1228  		goto done;
    1229  	}
    1230  
    1231  	mnt_table_set_parser_errcb(tb_new, parser_errcb);
    1232  
    1233  	fds[0].fd = fileno(f);
    1234  	fds[0].events = POLLPRI;
    1235  
    1236  	while (1) {
    1237  		struct libmnt_table *tmp;
    1238  		struct libmnt_fs *old, *new;
    1239  		int change, count;
    1240  
    1241  		count = poll(fds, 1, timeout);
    1242  		if (count == 0)
    1243  			break;	/* timeout */
    1244  		if (count < 0) {
    1245  			warn(_("poll() failed"));
    1246  			goto done;
    1247  		}
    1248  
    1249  		rewind(f);
    1250  		rc = mnt_table_parse_stream(tb_new, f, tabfile);
    1251  		if (!rc)
    1252  			rc = mnt_diff_tables(diff, tb, tb_new);
    1253  		if (rc < 0)
    1254  			goto done;
    1255  
    1256  		count = 0;
    1257  		mnt_reset_iter(itr, direction);
    1258  		while(mnt_tabdiff_next_change(
    1259  				diff, itr, &old, &new, &change) == 0) {
    1260  
    1261  			if (!has_poll_action(change))
    1262  				continue;
    1263  			if (!poll_match(new ? new : old))
    1264  				continue;
    1265  			count++;
    1266  			rc = !add_tabdiff_line(table, new, old, change);
    1267  			if (rc)
    1268  				goto done;
    1269  			if (flags & FL_FIRSTONLY)
    1270  				break;
    1271  		}
    1272  
    1273  		if (count) {
    1274  			rc = scols_table_print_range(table, NULL, NULL);
    1275  			if (rc == 0)
    1276  				fputc('\n', scols_table_get_stream(table));
    1277  			fflush(stdout);
    1278  			if (rc)
    1279  				goto done;
    1280  		}
    1281  
    1282  		/* swap tables */
    1283  		tmp = tb;
    1284  		tb = tb_new;
    1285  		tb_new = tmp;
    1286  
    1287  		/* remove already printed lines to reduce memory usage */
    1288  		scols_table_remove_lines(table);
    1289  		mnt_reset_table(tb_new);
    1290  
    1291  		if (count && (flags & FL_FIRSTONLY))
    1292  			break;
    1293  	}
    1294  
    1295  	rc = 0;
    1296  done:
    1297  	mnt_unref_table(tb_new);
    1298  	mnt_free_tabdiff(diff);
    1299  	mnt_free_iter(itr);
    1300  	if (f)
    1301  		fclose(f);
    1302  	return rc;
    1303  }
    1304  
    1305  static int uniq_fs_target_cmp(
    1306  		struct libmnt_table *tb __attribute__((__unused__)),
    1307  		struct libmnt_fs *a,
    1308  		struct libmnt_fs *b)
    1309  {
    1310  	return !mnt_fs_match_target(a, mnt_fs_get_target(b), cache);
    1311  }
    1312  
    1313  static void __attribute__((__noreturn__)) usage(void)
    1314  {
    1315  	FILE *out = stdout;
    1316  	size_t i;
    1317  
    1318  	fputs(USAGE_HEADER, out);
    1319  	fprintf(out, _(
    1320  	" %1$s [options]\n"
    1321  	" %1$s [options] <device> | <mountpoint>\n"
    1322  	" %1$s [options] <device> <mountpoint>\n"
    1323  	" %1$s [options] [--source <device>] [--target <path> | --mountpoint <dir>]\n"),
    1324  		program_invocation_short_name);
    1325  
    1326  	fputs(USAGE_SEPARATOR, out);
    1327  	fputs(_("Find a (mounted) filesystem.\n"), out);
    1328  
    1329  	fputs(USAGE_OPTIONS, out);
    1330  	fputs(_(" -s, --fstab            search in static table of filesystems\n"), out);
    1331  	fputs(_(" -m, --mtab             search in table of mounted filesystems\n"
    1332  		"                          (includes user space mount options)\n"), out);
    1333  	fputs(_(" -k, --kernel           search in kernel table of mounted\n"
    1334  		"                          filesystems (default)\n"), out);
    1335  	fputc('\n', out);
    1336  	fputs(_(" -p, --poll[=<list>]    monitor changes in table of mounted filesystems\n"), out);
    1337  	fputs(_(" -w, --timeout <num>    upper limit in milliseconds that --poll will block\n"), out);
    1338  	fputc('\n', out);
    1339  
    1340  	fputs(_(" -A, --all              disable all built-in filters, print all filesystems\n"), out);
    1341  	fputs(_(" -a, --ascii            use ASCII chars for tree formatting\n"), out);
    1342  	fputs(_(" -b, --bytes            print sizes in bytes rather than in human readable format\n"), out);
    1343  	fputs(_(" -C, --nocanonicalize   don't canonicalize when comparing paths\n"), out);
    1344  	fputs(_(" -c, --canonicalize     canonicalize printed paths\n"), out);
    1345  	fputs(_(" -D, --df               imitate the output of df(1)\n"), out);
    1346  	fputs(_(" -d, --direction <word> direction of search, 'forward' or 'backward'\n"), out);
    1347  	fputs(_(" -e, --evaluate         convert tags (LABEL,UUID,PARTUUID,PARTLABEL) \n"
    1348  	        "                          to device names\n"), out);
    1349  	fputs(_(" -F, --tab-file <path>  alternative file for -s, -m or -k options\n"), out);
    1350  	fputs(_(" -f, --first-only       print the first found filesystem only\n"), out);
    1351  	fputs(_(" -i, --invert           invert the sense of matching\n"), out);
    1352  	fputs(_(" -J, --json             use JSON output format\n"), out);
    1353  	fputs(_(" -l, --list             use list format output\n"), out);
    1354  	fputs(_(" -N, --task <tid>       use alternative namespace (/proc/<tid>/mountinfo file)\n"), out);
    1355  	fputs(_(" -n, --noheadings       don't print column headings\n"), out);
    1356  	fputs(_(" -O, --options <list>   limit the set of filesystems by mount options\n"), out);
    1357  	fputs(_(" -o, --output <list>    the output columns to be shown\n"), out);
    1358  	fputs(_("     --output-all       output all available columns\n"), out);
    1359  	fputs(_(" -P, --pairs            use key=\"value\" output format\n"), out);
    1360  	fputs(_("     --pseudo           print only pseudo-filesystems\n"), out);
    1361  	fputs(_("     --shadowed         print only filesystems over-mounted by another filesystem\n"), out);
    1362  	fputs(_(" -R, --submounts        print all submounts for the matching filesystems\n"), out);
    1363  	fputs(_(" -r, --raw              use raw output format\n"), out);
    1364  	fputs(_("     --real             print only real filesystems\n"), out);
    1365  	fputs(_(" -S, --source <string>  the device to mount (by name, maj:min, \n"
    1366  	        "                          LABEL=, UUID=, PARTUUID=, PARTLABEL=)\n"), out);
    1367  	fputs(_(" -T, --target <path>    the path to the filesystem to use\n"), out);
    1368  	fputs(_("     --tree             enable tree format output if possible\n"), out);
    1369  	fputs(_(" -M, --mountpoint <dir> the mountpoint directory\n"), out);
    1370  	fputs(_(" -t, --types <list>     limit the set of filesystems by FS types\n"), out);
    1371  	fputs(_(" -U, --uniq             ignore filesystems with duplicate target\n"), out);
    1372  	fputs(_(" -u, --notruncate       don't truncate text in columns\n"), out);
    1373  	fputs(_(" -v, --nofsroot         don't print [/dir] for bind or btrfs mounts\n"), out);
    1374  	fputs(_(" -y, --shell            use column names to be usable as shell variable identifiers\n"), out);
    1375  
    1376  	fputc('\n', out);
    1377  	fputs(_(" -x, --verify           verify mount table content (default is fstab)\n"), out);
    1378  	fputs(_("     --verbose          print more details\n"), out);
    1379  	fputs(_("     --vfs-all          print all VFS options\n"), out);
    1380  
    1381  	fputs(USAGE_SEPARATOR, out);
    1382  	printf(USAGE_HELP_OPTIONS(24));
    1383  
    1384  	fputs(USAGE_COLUMNS, out);
    1385  	for (i = 0; i < ARRAY_SIZE(infos); i++)
    1386  		fprintf(out, " %11s  %s\n", infos[i].name, _(infos[i].help));
    1387  
    1388  	printf(USAGE_MAN_TAIL("findmnt(8)"));
    1389  
    1390  	exit(EXIT_SUCCESS);
    1391  }
    1392  
    1393  int main(int argc, char *argv[])
    1394  {
    1395  	struct libmnt_table *tb = NULL;
    1396  	char **tabfiles = NULL;
    1397  	int direction = MNT_ITER_FORWARD;
    1398  	int verify = 0;
    1399  	int c, rc = -1, timeout = -1;
    1400  	int ntabfiles = 0, tabtype = 0;
    1401  	char *outarg = NULL;
    1402  	size_t i;
    1403  	int force_tree = 0, istree = 0;
    1404  
    1405  	struct libscols_table *table = NULL;
    1406  
    1407  	enum {
    1408  		FINDMNT_OPT_VERBOSE = CHAR_MAX + 1,
    1409  		FINDMNT_OPT_TREE,
    1410  		FINDMNT_OPT_OUTPUT_ALL,
    1411  		FINDMNT_OPT_PSEUDO,
    1412  		FINDMNT_OPT_REAL,
    1413  		FINDMNT_OPT_VFS_ALL,
    1414  		FINDMNT_OPT_SHADOWED
    1415  	};
    1416  
    1417  	static const struct option longopts[] = {
    1418  		{ "all",	    no_argument,       NULL, 'A'		 },
    1419  		{ "ascii",	    no_argument,       NULL, 'a'		 },
    1420  		{ "bytes",	    no_argument,       NULL, 'b'		 },
    1421  		{ "canonicalize",   no_argument,       NULL, 'c'		 },
    1422  		{ "direction",	    required_argument, NULL, 'd'		 },
    1423  		{ "df",		    no_argument,       NULL, 'D'		 },
    1424  		{ "evaluate",	    no_argument,       NULL, 'e'		 },
    1425  		{ "first-only",	    no_argument,       NULL, 'f'		 },
    1426  		{ "fstab",	    no_argument,       NULL, 's'		 },
    1427  		{ "help",	    no_argument,       NULL, 'h'		 },
    1428  		{ "invert",	    no_argument,       NULL, 'i'		 },
    1429  		{ "json",	    no_argument,       NULL, 'J'		 },
    1430  		{ "kernel",	    no_argument,       NULL, 'k'		 },
    1431  		{ "list",	    no_argument,       NULL, 'l'		 },
    1432  		{ "mountpoint",	    required_argument, NULL, 'M'		 },
    1433  		{ "mtab",	    no_argument,       NULL, 'm'		 },
    1434  		{ "noheadings",	    no_argument,       NULL, 'n'		 },
    1435  		{ "notruncate",	    no_argument,       NULL, 'u'		 },
    1436  		{ "options",	    required_argument, NULL, 'O'		 },
    1437  		{ "output",	    required_argument, NULL, 'o'		 },
    1438  		{ "output-all",	    no_argument,       NULL, FINDMNT_OPT_OUTPUT_ALL },
    1439  		{ "poll",	    optional_argument, NULL, 'p'		 },
    1440  		{ "pairs",	    no_argument,       NULL, 'P'		 },
    1441  		{ "raw",	    no_argument,       NULL, 'r'		 },
    1442  		{ "types",	    required_argument, NULL, 't'		 },
    1443  		{ "nocanonicalize", no_argument,       NULL, 'C'		 },
    1444  		{ "nofsroot",	    no_argument,       NULL, 'v'		 },
    1445  		{ "submounts",	    no_argument,       NULL, 'R'		 },
    1446  		{ "source",	    required_argument, NULL, 'S'		 },
    1447  		{ "tab-file",	    required_argument, NULL, 'F'		 },
    1448  		{ "task",	    required_argument, NULL, 'N'		 },
    1449  		{ "target",	    required_argument, NULL, 'T'		 },
    1450  		{ "timeout",	    required_argument, NULL, 'w'		 },
    1451  		{ "uniq",	    no_argument,       NULL, 'U'		 },
    1452  		{ "verify",	    no_argument,       NULL, 'x'		 },
    1453  		{ "version",	    no_argument,       NULL, 'V'		 },
    1454  		{ "shell",          no_argument,       NULL, 'y'                 },
    1455  		{ "verbose",	    no_argument,       NULL, FINDMNT_OPT_VERBOSE },
    1456  		{ "tree",	    no_argument,       NULL, FINDMNT_OPT_TREE	 },
    1457  		{ "real",	    no_argument,       NULL, FINDMNT_OPT_REAL	 },
    1458  		{ "pseudo",	    no_argument,       NULL, FINDMNT_OPT_PSEUDO	 },
    1459  		{ "vfs-all",	    no_argument,       NULL, FINDMNT_OPT_VFS_ALL },
    1460  		{ "shadowed",       no_argument,       NULL, FINDMNT_OPT_SHADOWED },
    1461  		{ NULL, 0, NULL, 0 }
    1462  	};
    1463  
    1464  	static const ul_excl_t excl[] = {	/* rows and cols in ASCII order */
    1465  		{ 'C', 'c'},			/* [no]canonicalize */
    1466  		{ 'C', 'e' },			/* nocanonicalize, evaluate */
    1467  		{ 'J', 'P', 'r','x' },		/* json,pairs,raw,verify */
    1468  		{ 'M', 'T' },			/* mountpoint, target */
    1469  		{ 'N','k','m','s' },		/* task,kernel,mtab,fstab */
    1470  		{ 'P','l','r','x' },		/* pairs,list,raw,verify */
    1471  		{ 'p','x' },			/* poll,verify */
    1472  		{ 'm','p','s' },		/* mtab,poll,fstab */
    1473  		{ FINDMNT_OPT_PSEUDO, FINDMNT_OPT_REAL },
    1474  		{ 0 }
    1475  	};
    1476  	int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
    1477  
    1478  	setlocale(LC_ALL, "");
    1479  	bindtextdomain(PACKAGE, LOCALEDIR);
    1480  	textdomain(PACKAGE);
    1481  	close_stdout_atexit();
    1482  
    1483  	/* default output format */
    1484  	flags |= FL_TREE;
    1485  
    1486  	while ((c = getopt_long(argc, argv,
    1487  				"AabCcDd:ehiJfF:o:O:p::PklmM:nN:rst:uvRS:T:Uw:Vxy",
    1488  				longopts, NULL)) != -1) {
    1489  
    1490  		err_exclusive_options(c, longopts, excl, excl_st);
    1491  
    1492  		switch(c) {
    1493  		case 'A':
    1494  			flags |= FL_ALL;
    1495  			break;
    1496  		case 'a':
    1497  			flags |= FL_ASCII;
    1498  			break;
    1499  		case 'b':
    1500  			flags |= FL_BYTES;
    1501  			break;
    1502  		case 'C':
    1503  			flags |= FL_NOCACHE;
    1504  			break;
    1505  		case 'c':
    1506  			flags |= FL_CANONICALIZE;
    1507  			break;
    1508  		case 'D':
    1509  			flags &= ~FL_TREE;
    1510  			flags |= FL_DF;
    1511  			break;
    1512  		case 'd':
    1513  			if (!strcmp(optarg, "forward"))
    1514  				direction = MNT_ITER_FORWARD;
    1515  			else if (!strcmp(optarg, "backward"))
    1516  				direction = MNT_ITER_BACKWARD;
    1517  			else
    1518  				errx(EXIT_FAILURE,
    1519  					_("unknown direction '%s'"), optarg);
    1520  			break;
    1521  		case 'e':
    1522  			flags |= FL_EVALUATE;
    1523  			break;
    1524  		case 'i':
    1525  			flags |= FL_INVERT;
    1526  			break;
    1527  		case 'J':
    1528  			flags |= FL_JSON;
    1529  			break;
    1530  		case 'f':
    1531  			flags |= FL_FIRSTONLY;
    1532  			break;
    1533  		case 'F':
    1534  			tabfiles = append_tabfile(tabfiles, &ntabfiles, optarg);
    1535  			break;
    1536  		case 'u':
    1537  			disable_columns_truncate();
    1538  			break;
    1539  		case 'o':
    1540  			outarg = optarg;
    1541  			break;
    1542  		case FINDMNT_OPT_OUTPUT_ALL:
    1543  			ncolumns = 0;
    1544  			for (i = 0; i < ARRAY_SIZE(infos); i++) {
    1545  				if (is_tabdiff_column(i))
    1546  					continue;
    1547  				columns[ncolumns++] = i;
    1548  			}
    1549  			break;
    1550  		case 'O':
    1551  			set_match(COL_OPTIONS, optarg);
    1552  			break;
    1553  		case 'p':
    1554  			if (optarg) {
    1555  				nactions = string_to_idarray(optarg,
    1556  						actions, ARRAY_SIZE(actions),
    1557  						poll_action_name_to_id);
    1558  				if (nactions < 0)
    1559  					exit(EXIT_FAILURE);
    1560  			}
    1561  			flags |= FL_POLL;
    1562  			flags &= ~FL_TREE;
    1563  			break;
    1564  		case 'P':
    1565  			flags |= FL_EXPORT;
    1566  			flags &= ~FL_TREE;
    1567  			break;
    1568  		case 'm':		/* mtab */
    1569  			tabtype = TABTYPE_MTAB;
    1570  			flags &= ~FL_TREE;
    1571  			break;
    1572  		case 's':		/* fstab */
    1573  			tabtype = TABTYPE_FSTAB;
    1574  			flags &= ~FL_TREE;
    1575  			break;
    1576  		case 'k':		/* kernel (mountinfo) */
    1577  			tabtype = TABTYPE_KERNEL;
    1578  			break;
    1579  		case 't':
    1580  			set_match(COL_FSTYPE, optarg);
    1581  			break;
    1582  		case 'r':
    1583  			flags &= ~FL_TREE;	/* disable the default */
    1584  			flags |= FL_RAW;	/* enable raw */
    1585  			break;
    1586  		case 'l':
    1587  			flags &= ~FL_TREE;	/* disable the default */
    1588  			break;
    1589  		case 'n':
    1590  			flags |= FL_NOHEADINGS;
    1591  			break;
    1592  		case 'N':
    1593  			tabtype = TABTYPE_KERNEL;
    1594  			tabfiles = append_pid_tabfile(tabfiles, &ntabfiles,
    1595  					strtou32_or_err(optarg,
    1596  						_("invalid TID argument")));
    1597  			break;
    1598  		case 'v':
    1599  			flags |= FL_NOFSROOT;
    1600  			break;
    1601  		case 'R':
    1602  			flags |= FL_SUBMOUNTS;
    1603  			break;
    1604  		case 'S':
    1605  			set_source_match(optarg);
    1606  			flags |= FL_NOSWAPMATCH;
    1607  			break;
    1608  		case 'M':
    1609  			flags |= FL_STRICTTARGET;
    1610  			/* fallthrough */
    1611  		case 'T':
    1612  			set_match(COL_TARGET, optarg);
    1613  			flags |= FL_NOSWAPMATCH;
    1614  			break;
    1615  		case 'U':
    1616  			flags |= FL_UNIQ;
    1617  			break;
    1618  		case 'w':
    1619  			timeout = strtos32_or_err(optarg, _("invalid timeout argument"));
    1620  			break;
    1621  		case 'x':
    1622  			verify = 1;
    1623  			break;
    1624  		case 'y':
    1625  			flags |= FL_SHELLVAR;
    1626  			break;
    1627  		case FINDMNT_OPT_VERBOSE:
    1628  			flags |= FL_VERBOSE;
    1629  			break;
    1630  		case FINDMNT_OPT_TREE:
    1631  			force_tree = 1;
    1632  			break;
    1633  		case FINDMNT_OPT_PSEUDO:
    1634  			flags |= FL_PSEUDO;
    1635  			break;
    1636  		case FINDMNT_OPT_REAL:
    1637  			flags |= FL_REAL;
    1638  			break;
    1639  		case FINDMNT_OPT_VFS_ALL:
    1640  			flags |= FL_VFS_ALL;
    1641  			break;
    1642  		case FINDMNT_OPT_SHADOWED:
    1643  			flags |= FL_SHADOWED;
    1644  			break;
    1645  		case 'h':
    1646  			usage();
    1647  		case 'V':
    1648  			print_version(EXIT_SUCCESS);
    1649  		default:
    1650  			errtryhelp(EXIT_FAILURE);
    1651  		}
    1652  	}
    1653  
    1654  	if (!ncolumns && (flags & FL_DF)) {
    1655  		add_column(columns, ncolumns++, COL_SOURCE);
    1656  		add_column(columns, ncolumns++, COL_FSTYPE);
    1657  		add_column(columns, ncolumns++, COL_SIZE);
    1658  		add_column(columns, ncolumns++, COL_USED);
    1659  		add_column(columns, ncolumns++, COL_AVAIL);
    1660  		add_column(columns, ncolumns++, COL_USEPERC);
    1661  		add_column(columns, ncolumns++, COL_TARGET);
    1662  	}
    1663  
    1664  	/* default columns */
    1665  	if (!ncolumns) {
    1666  		if (flags & FL_POLL)
    1667  			add_column(columns, ncolumns++, COL_ACTION);
    1668  
    1669  		add_column(columns, ncolumns++, COL_TARGET);
    1670  		add_column(columns, ncolumns++, COL_SOURCE);
    1671  		add_column(columns, ncolumns++, COL_FSTYPE);
    1672  		add_column(columns, ncolumns++, COL_OPTIONS);
    1673  	}
    1674  
    1675  	if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
    1676  					 &ncolumns, column_name_to_id) < 0)
    1677  		return EXIT_FAILURE;
    1678  
    1679  	if (!tabtype)
    1680  		tabtype = verify ? TABTYPE_FSTAB : TABTYPE_KERNEL;
    1681  
    1682  	if ((flags & FL_POLL) && ntabfiles > 1)
    1683  		errx(EXIT_FAILURE, _("--poll accepts only one file, but more specified by --tab-file"));
    1684  
    1685  	if (optind < argc && (get_match(COL_SOURCE) || get_match(COL_TARGET)))
    1686  		errx(EXIT_FAILURE, _(
    1687  			"options --target and --source can't be used together "
    1688  			"with command line element that is not an option"));
    1689  
    1690  	if (optind < argc)
    1691  		set_source_match(argv[optind++]);	/* dev/tag/mountpoint/maj:min */
    1692  	if (optind < argc)
    1693  		set_match(COL_TARGET, argv[optind++]);	/* mountpoint */
    1694  
    1695  	if ((flags & FL_SUBMOUNTS) && is_listall_mode())
    1696  		/* don't care about submounts if list all mounts */
    1697  		flags &= ~FL_SUBMOUNTS;
    1698  
    1699  	if (!(flags & FL_SUBMOUNTS) && ((flags & FL_FIRSTONLY)
    1700  	    || get_match(COL_TARGET)
    1701  	    || get_match(COL_SOURCE)
    1702  	    || get_match(COL_MAJMIN)))
    1703  		flags &= ~FL_TREE;
    1704  
    1705  	if (!(flags & FL_NOSWAPMATCH) &&
    1706  	    !get_match(COL_TARGET) && get_match(COL_SOURCE)) {
    1707  		/*
    1708  		 * Check if we can swap source and target, it's
    1709  		 * not possible if the source is LABEL=/UUID=
    1710  		 */
    1711  		const char *x = get_match(COL_SOURCE);
    1712  
    1713  		if (!strncmp(x, "LABEL=", 6) || !strncmp(x, "UUID=", 5) ||
    1714  		    !strncmp(x, "PARTLABEL=", 10) || !strncmp(x, "PARTUUID=", 9))
    1715  			flags |= FL_NOSWAPMATCH;
    1716  	}
    1717  
    1718  	/*
    1719  	 * initialize libmount
    1720  	 */
    1721  	mnt_init_debug(0);
    1722  
    1723  	tb = parse_tabfiles(tabfiles, ntabfiles, tabtype);
    1724  	if (!tb)
    1725  		goto leave;
    1726  
    1727  	if (tabtype == TABTYPE_MTAB && tab_is_kernel(tb))
    1728  		tabtype = TABTYPE_KERNEL;
    1729  
    1730  	istree = tab_is_tree(tb);
    1731  	if (istree && force_tree)
    1732  		flags |= FL_TREE;
    1733  
    1734  	if ((flags & FL_TREE) && (ntabfiles > 1 || !istree))
    1735  		flags &= ~FL_TREE;
    1736  
    1737  	if (!(flags & FL_NOCACHE)) {
    1738  		cache = mnt_new_cache();
    1739  		if (!cache) {
    1740  			warn(_("failed to initialize libmount cache"));
    1741  			goto leave;
    1742  		}
    1743  		mnt_table_set_cache(tb, cache);
    1744  
    1745  		if (tabtype != TABTYPE_KERNEL)
    1746  			cache_set_targets(cache);
    1747  	}
    1748  
    1749  	if (flags & FL_UNIQ)
    1750  		mnt_table_uniq_fs(tb, MNT_UNIQ_KEEPTREE, uniq_fs_target_cmp);
    1751  
    1752  	if (verify) {
    1753  		rc = verify_table(tb);
    1754  		goto leave;
    1755  	}
    1756  
    1757  	/*
    1758  	 * initialize libsmartcols
    1759  	 */
    1760  	scols_init_debug(0);
    1761  	table = scols_new_table();
    1762  	if (!table) {
    1763  		warn(_("failed to allocate output table"));
    1764  		goto leave;
    1765  	}
    1766  	scols_table_enable_raw(table,        !!(flags & FL_RAW));
    1767  	scols_table_enable_export(table,     !!(flags & FL_EXPORT));
    1768  	scols_table_enable_shellvar(table,   !!(flags & FL_SHELLVAR));
    1769  	scols_table_enable_json(table,       !!(flags & FL_JSON));
    1770  	scols_table_enable_ascii(table,      !!(flags & FL_ASCII));
    1771  	scols_table_enable_noheadings(table, !!(flags & FL_NOHEADINGS));
    1772  
    1773  	if (flags & FL_JSON)
    1774  		scols_table_set_name(table, "filesystems");
    1775  
    1776  	for (i = 0; i < ncolumns; i++) {
    1777  		struct libscols_column *cl;
    1778  		int fl = get_column_flags(i);
    1779  		int id = get_column_id(i);
    1780  
    1781  		if (!(flags & FL_TREE))
    1782  			fl &= ~SCOLS_FL_TREE;
    1783  
    1784  		if (!(flags & FL_POLL) && is_tabdiff_column(id)) {
    1785  			warnx(_("%s column is requested, but --poll "
    1786  			       "is not enabled"), get_column_name(i));
    1787  			goto leave;
    1788  		}
    1789  		cl = scols_table_new_column(table, get_column_name(i),
    1790  					get_column_whint(i), fl);
    1791  		if (!cl)	{
    1792  			warn(_("failed to allocate output column"));
    1793  			goto leave;
    1794  		}
    1795  		/* multi-line cells (now used for SOURCES) */
    1796  		if (fl & SCOLS_FL_WRAP) {
    1797  			scols_column_set_wrapfunc(cl,
    1798  						scols_wrapnl_chunksize,
    1799  						scols_wrapnl_nextchunk,
    1800  						NULL);
    1801  			scols_column_set_safechars(cl, "\n");
    1802  		}
    1803  		if (flags & FL_JSON) {
    1804  			switch (id) {
    1805  			case COL_SIZE:
    1806  			case COL_AVAIL:
    1807  			case COL_USED:
    1808  				if (!(flags & FL_BYTES))
    1809  					break;
    1810  				/* fallthrough */
    1811  			case COL_ID:
    1812  			case COL_PARENT:
    1813  			case COL_FREQ:
    1814  			case COL_PASSNO:
    1815  			case COL_TID:
    1816  				scols_column_set_json_type(cl, SCOLS_JSON_NUMBER);
    1817  				break;
    1818  			default:
    1819  				if (fl & SCOLS_FL_WRAP)
    1820  					scols_column_set_json_type(cl, SCOLS_JSON_ARRAY_STRING);
    1821  				else
    1822  					scols_column_set_json_type(cl, SCOLS_JSON_STRING);
    1823  				break;
    1824  			}
    1825  		}
    1826  	}
    1827  
    1828  	/*
    1829  	 * Fill in data to the output table
    1830  	 */
    1831  	if (flags & FL_POLL) {
    1832  		/* poll mode (accept the first tabfile only) */
    1833  		rc = poll_table(tb, tabfiles ? *tabfiles : _PATH_PROC_MOUNTINFO, timeout, table, direction);
    1834  
    1835  	} else if ((flags & FL_TREE) && !(flags & FL_SUBMOUNTS)) {
    1836  		/* whole tree */
    1837  		rc = create_treenode(table, tb, NULL, NULL);
    1838  	} else {
    1839  		/* whole list of sub-tree */
    1840  		rc = add_matching_lines(tb, table, direction);
    1841  
    1842  		if (rc != 0
    1843  		    && tabtype == TABTYPE_KERNEL
    1844  		    && (flags & FL_NOSWAPMATCH)
    1845  		    && !(flags & FL_STRICTTARGET)
    1846  		    && get_match(COL_TARGET)) {
    1847  			/*
    1848  			 * Found nothing, maybe the --target is regular file,
    1849  			 * try it again with extra functionality for target
    1850  			 * match
    1851  			 */
    1852  			enable_extra_target_match(tb);
    1853  			rc = add_matching_lines(tb, table, direction);
    1854  		}
    1855  	}
    1856  
    1857  	/*
    1858  	 * Print the output table for non-poll modes
    1859  	 */
    1860  	if (!rc && !(flags & FL_POLL))
    1861  		scols_print_table(table);
    1862  leave:
    1863  	scols_unref_table(table);
    1864  
    1865  	mnt_unref_table(tb);
    1866  	mnt_unref_cache(cache);
    1867  
    1868  	free(tabfiles);
    1869  #ifdef HAVE_LIBUDEV
    1870  	udev_unref(udev);
    1871  #endif
    1872  	return rc ? EXIT_FAILURE : EXIT_SUCCESS;
    1873  }