]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - misc-utils/findmnt.c
docs: update year in libs docs
[thirdparty/util-linux.git] / misc-utils / findmnt.c
index 3386061cb6a5183ba257826c4c7bfda78815db03..a4b7a1b3a9cf10c854de304699fb6a6f19159e45 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * findmnt(8)
  *
- * Copyright (C) 2010-2014 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2010-2015 Red Hat, Inc. All rights reserved.
  * Written by Karel Zak <kzak@redhat.com>
  *
  * This program is free software; you can redistribute it and/or modify
 #include "optutils.h"
 #include "mangle.h"
 
-/* flags */
-enum {
-       FL_EVALUATE     = (1 << 1),
-       FL_CANONICALIZE = (1 << 2),
-       FL_FIRSTONLY    = (1 << 3),
-       FL_INVERT       = (1 << 4),
-       FL_NOSWAPMATCH  = (1 << 6),
-       FL_NOFSROOT     = (1 << 7),
-       FL_SUBMOUNTS    = (1 << 8),
-       FL_POLL         = (1 << 9),
-       FL_DF           = (1 << 10),
-       FL_ALL          = (1 << 11),
-       FL_UNIQ         = (1 << 12),
-       FL_BYTES        = (1 << 13),
-
-       /* basic table settings */
-       FL_ASCII        = (1 << 20),
-       FL_RAW          = (1 << 21),
-       FL_NOHEADINGS   = (1 << 22),
-       FL_EXPORT       = (1 << 23),
-       FL_TREE         = (1 << 24)
-};
+#include "findmnt.h"
 
 /* column IDs */
 enum {
@@ -96,9 +75,7 @@ enum {
        COL_OPT_FIELDS,
        COL_PROPAGATION,
        COL_FREQ,
-       COL_PASSNO,
-
-       FINDMNT_NCOLUMNS
+       COL_PASSNO
 };
 
 enum {
@@ -118,7 +95,7 @@ struct colinfo {
 };
 
 /* columns descriptions (don't use const, this is writable) */
-static struct colinfo infos[FINDMNT_NCOLUMNS] = {
+static struct colinfo infos[] = {
        [COL_SOURCE]       = { "SOURCE",       0.25, SCOLS_FL_NOEXTREMES, N_("source device") },
        [COL_TARGET]       = { "TARGET",       0.30, SCOLS_FL_TREE| SCOLS_FL_NOEXTREMES, N_("mountpoint") },
        [COL_FSTYPE]       = { "FSTYPE",       0.10, SCOLS_FL_TRUNC, N_("filesystem type") },
@@ -146,23 +123,38 @@ static struct colinfo infos[FINDMNT_NCOLUMNS] = {
        [COL_PASSNO]       = { "PASSNO",          1, SCOLS_FL_RIGHT, N_("pass number on parallel fsck(8) [fstab only]") }
 };
 
-/* global flags */
-static int flags;
+/* columns[] array specifies all currently wanted output column. The columns
+ * are defined by infos[] array and you can specify (on command line) each
+ * column twice. That's enough, dynamically allocated array of the columns is
+ * unnecessary overkill and over-engineering in this case */
+static int columns[ARRAY_SIZE(infos) * 2];
+static size_t ncolumns;
+
+static inline size_t err_columns_index(size_t arysz, size_t idx)
+{
+       if (idx >= arysz)
+               errx(EXIT_FAILURE, _("too many columns specified, "
+                                    "the limit is %zu columns"),
+                               arysz - 1);
+       return idx;
+}
 
-/* array with IDs of enabled columns */
-static int columns[FINDMNT_NCOLUMNS];
-static int ncolumns;
+#define add_column(ary, n, id) \
+               ((ary)[ err_columns_index(ARRAY_SIZE(ary), (n)) ] = (id))
 
 /* poll actions (parsed --poll=<list> */
 #define FINDMNT_NACTIONS       4               /* mount, umount, move, remount */
 static int actions[FINDMNT_NACTIONS];
 static int nactions;
 
-/* libmount cache */
-static struct libmnt_cache *cache;
+/* global (accessed from findmnt-verify.c too) */
+int flags;
+int parse_nerrors;
+struct libmnt_cache *cache;
+
 
 #ifdef HAVE_LIBUDEV
-struct udev *udev;
+static struct udev *udev;
 #endif
 
 static int match_func(struct libmnt_fs *fs, void *data __attribute__ ((__unused__)));
@@ -170,8 +162,9 @@ static int match_func(struct libmnt_fs *fs, void *data __attribute__ ((__unused_
 
 static int get_column_id(int num)
 {
-       assert(num < ncolumns);
-       assert(columns[num] < FINDMNT_NCOLUMNS);
+       assert(num >= 0);
+       assert((size_t) num < ncolumns);
+       assert((size_t) columns[num] < ARRAY_SIZE(infos));
        return columns[num];
 }
 
@@ -182,7 +175,7 @@ static struct colinfo *get_column_info(int num)
 
 static const char *column_id_to_name(int id)
 {
-       assert(id < FINDMNT_NCOLUMNS);
+       assert((size_t) id < ARRAY_SIZE(infos));
        return infos[id].name;
 }
 
@@ -203,25 +196,25 @@ static int get_column_flags(int num)
 
 static const char *get_match(int id)
 {
-       assert(id < FINDMNT_NCOLUMNS);
+       assert((size_t) id < ARRAY_SIZE(infos));
        return infos[id].match;
 }
 
 static void *get_match_data(int id)
 {
-       assert(id < FINDMNT_NCOLUMNS);
+       assert((size_t) id < ARRAY_SIZE(infos));
        return infos[id].match_data;
 }
 
 static void set_match(int id, const char *match)
 {
-       assert(id < FINDMNT_NCOLUMNS);
+       assert((size_t) id < ARRAY_SIZE(infos));
        infos[id].match = match;
 }
 
 static void set_match_data(int id, void *data)
 {
-       assert(id < FINDMNT_NCOLUMNS);
+       assert((size_t) id < ARRAY_SIZE(infos));
        infos[id].match_data = data;
 }
 
@@ -244,30 +237,44 @@ static void set_source_match(const char *data)
                set_match(COL_SOURCE, data);
 }
 
-static void enable_extra_target_match(void)
+/*
+ * Extra functionality for --target <path>. The function mnt_table_find_mountpoint()
+ * also checks parents (path elements in reverse order) to get mountpoint.
+ *
+ * @tb has to be from kernel (so no fstab or so)!
+ */
+static void enable_extra_target_match(struct libmnt_table *tb)
 {
-       char *cn = NULL, *mnt = NULL;
+       char *cn = NULL;
+       const char *tgt = NULL, *mnt = NULL;
+       struct libmnt_fs *fs;
 
        /*
         * Check if match pattern is mountpoint, if not use the
         * real mountpoint.
         */
-       cn = mnt_resolve_path(get_match(COL_TARGET), cache);
-       if (!cn)
-               return;
+       if (flags & FL_NOCACHE)
+               tgt = get_match(COL_TARGET);
+       else {
+               tgt = cn = mnt_resolve_path(get_match(COL_TARGET), cache);
+               if (!cn)
+                       return;
+       }
 
-       mnt = mnt_get_mountpoint(cn);
-       if (!mnt || strcmp(mnt, cn) == 0)
-               return;
+       fs = mnt_table_find_mountpoint(tb, tgt, MNT_ITER_BACKWARD);
+       if (fs)
+               mnt = mnt_fs_get_target(fs);
+       if (mnt && strcmp(mnt, tgt) != 0)
+               set_match(COL_TARGET, xstrdup(mnt));    /* replace the current setting */
 
-       /* replace the current setting with the real mountpoint */
-       set_match(COL_TARGET, mnt);
+       if (!cache)
+               free(cn);
 }
 
 
 static int is_tabdiff_column(int id)
 {
-       assert(id < FINDMNT_NCOLUMNS);
+       assert((size_t) id < ARRAY_SIZE(infos));
 
        switch(id) {
        case COL_ACTION:
@@ -283,9 +290,9 @@ static int is_tabdiff_column(int id)
 /*
  * "findmnt" without any filter
  */
-static int is_listall_mode(void)
+int is_listall_mode(void)
 {
-       if ((flags & FL_DF) && !(flags & FL_ALL))
+       if ((flags & FL_DF || flags & FL_REAL || flags & FL_PSEUDO) && !(flags & FL_ALL))
                return 0;
 
        return (!get_match(COL_SOURCE) &&
@@ -347,9 +354,9 @@ static int is_mount_compatible_mode(void)
 
 static void disable_columns_truncate(void)
 {
-       int i;
+       size_t i;
 
-       for (i = 0; i < FINDMNT_NCOLUMNS; i++)
+       for (i = 0; i < ARRAY_SIZE(infos); i++)
                infos[i].flags &= ~SCOLS_FL_TRUNC;
 }
 
@@ -358,9 +365,9 @@ static void disable_columns_truncate(void)
  */
 static int column_name_to_id(const char *name, size_t namesz)
 {
-       int i;
+       size_t i;
 
-       for (i = 0; i < FINDMNT_NCOLUMNS; i++) {
+       for (i = 0; i < ARRAY_SIZE(infos); i++) {
                const char *cn = column_id_to_name(i);
 
                if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
@@ -439,7 +446,7 @@ static char *get_tag(struct libmnt_fs *fs, const char *tagname, int col
        else {
                const char *dev = mnt_fs_get_source(fs);
 
-               if (dev)
+               if (dev && !(flags & FL_NOCACHE))
                        dev = mnt_resolve_spec(dev, cache);
 #ifdef HAVE_LIBUDEV
                if (dev)
@@ -507,19 +514,22 @@ static char *get_data(struct libmnt_fs *fs, int num)
        {
                const char *root = mnt_fs_get_root(fs);
                const char *spec = mnt_fs_get_srcpath(fs);
+               char *cn = NULL;
 
                if (spec && (flags & FL_CANONICALIZE))
-                       spec = mnt_resolve_path(spec, cache);
+                       spec = cn = mnt_resolve_path(spec, cache);
                if (!spec) {
                        spec = mnt_fs_get_source(fs);
 
                        if (spec && (flags & FL_EVALUATE))
-                               spec = mnt_resolve_spec(spec, cache);
+                               spec = cn = mnt_resolve_spec(spec, cache);
                }
                if (root && spec && !(flags & FL_NOFSROOT) && strcmp(root, "/"))
                        xasprintf(&str, "%s[%s]", spec, root);
                else if (spec)
                        str = xstrdup(spec);
+               if (!cache)
+                       free(cn);
                break;
        }
        case COL_TARGET:
@@ -559,7 +569,7 @@ static char *get_data(struct libmnt_fs *fs, int num)
                if (!devno)
                        break;
 
-               if ((flags & FL_RAW) || (flags & FL_EXPORT))
+               if ((flags & FL_RAW) || (flags & FL_EXPORT) || (flags & FL_JSON))
                        xasprintf(&str, "%u:%u", major(devno), minor(devno));
                else
                        xasprintf(&str, "%3u:%-3u", major(devno), minor(devno));
@@ -671,15 +681,16 @@ static char *get_tabdiff_data(struct libmnt_fs *old_fs,
 static struct libscols_line *add_line(struct libscols_table *table, struct libmnt_fs *fs,
                                        struct libscols_line *parent)
 {
-       int i;
+       size_t i;
        struct libscols_line *line = scols_table_new_line(table, parent);
 
-       if (!line) {
-               warn(_("failed to add line to output"));
-               return NULL;
+       if (!line)
+               err(EXIT_FAILURE, _("failed to allocate output line"));
+
+       for (i = 0; i < ncolumns; i++) {
+               if (scols_line_refer_data(line, i, get_data(fs, i)))
+                       err(EXIT_FAILURE, _("failed to add output data"));
        }
-       for (i = 0; i < ncolumns; i++)
-               scols_line_refer_data(line, i, get_data(fs, i));
 
        scols_line_set_userdata(line, fs);
        return line;
@@ -688,16 +699,17 @@ static struct libscols_line *add_line(struct libscols_table *table, struct libmn
 static struct libscols_line *add_tabdiff_line(struct libscols_table *table, struct libmnt_fs *new_fs,
                        struct libmnt_fs *old_fs, int change)
 {
-       int i;
+       size_t i;
        struct libscols_line *line = scols_table_new_line(table, NULL);
 
-       if (!line) {
-               warn(_("failed to add line to output"));
-               return NULL;
+       if (!line)
+               err(EXIT_FAILURE, _("failed to allocate output line"));
+
+       for (i = 0; i < ncolumns; i++) {
+               if (scols_line_refer_data(line, i,
+                               get_tabdiff_data(old_fs, new_fs, change, i)))
+                       err(EXIT_FAILURE, _("failed to add output data"));
        }
-       for (i = 0; i < ncolumns; i++)
-               scols_line_refer_data(line, i,
-                               get_tabdiff_data(old_fs, new_fs, change, i));
 
        return line;
 }
@@ -706,16 +718,21 @@ static int has_line(struct libscols_table *table, struct libmnt_fs *fs)
 {
        struct libscols_line *ln;
        struct libscols_iter *itr;
+       int rc = 0;
 
        itr = scols_new_iter(SCOLS_ITER_FORWARD);
        if (!itr)
                return 0;
 
-       while(scols_table_next_line(table, itr, &ln) == 0) {
-               if ((struct libmnt_fs *) scols_line_get_userdata(ln) == fs)
-                       return 1;
+       while (scols_table_next_line(table, itr, &ln) == 0) {
+               if ((struct libmnt_fs *) scols_line_get_userdata(ln) == fs) {
+                       rc = 1;
+                       break;
+               }
        }
-       return 0;
+
+       scols_free_iter(itr);
+       return rc;
 }
 
 /* reads filesystems from @tb (libmount) and fillin @table (output table) */
@@ -764,8 +781,9 @@ leave:
 static int parser_errcb(struct libmnt_table *tb __attribute__ ((__unused__)),
                        const char *filename, int line)
 {
-       warnx(_("%s: parse error at line %d"), filename, line);
-       return 0;
+       warnx(_("%s: parse error at line %d -- ignored"), filename, line);
+       ++parse_nerrors;
+       return 1;
 }
 
 static char **append_tabfile(char **files, int *nfiles, char *filename)
@@ -828,11 +846,34 @@ static struct libmnt_table *parse_tabfiles(char **files,
        return tb;
 }
 
+/*
+ * Parses mountinfo and calls mnt_cache_set_targets(cache, mtab). Only
+ * necessary if @tb in main() was read from a non-kernel source.
+ */
+static void cache_set_targets(struct libmnt_cache *tmp)
+{
+       struct libmnt_table *tb;
+       const char *path;
+
+       tb = mnt_new_table();
+       if (!tb)
+               return;
+
+       path = access(_PATH_PROC_MOUNTINFO, R_OK) == 0 ?
+               _PATH_PROC_MOUNTINFO :
+               _PATH_PROC_MOUNTS;
+
+       if (mnt_table_parse_file(tb, path) == 0)
+               mnt_cache_set_targets(tmp, tb);
+
+       mnt_unref_table(tb);
+}
+
 /* checks if @tb contains parent->child relations */
 static int tab_is_tree(struct libmnt_table *tb)
 {
        struct libmnt_fs *fs = NULL;
-       struct libmnt_iter *itr = NULL;
+       struct libmnt_iter *itr;
        int rc = 0;
 
        itr = mnt_new_iter(MNT_ITER_BACKWARD);
@@ -847,6 +888,26 @@ static int tab_is_tree(struct libmnt_table *tb)
        return rc;
 }
 
+/* checks if all fs in @tb are from kernel */
+static int tab_is_kernel(struct libmnt_table *tb)
+{
+       struct libmnt_fs *fs = NULL;
+       struct libmnt_iter *itr;
+
+       itr = mnt_new_iter(MNT_ITER_BACKWARD);
+       if (!itr)
+               return 0;
+
+       while (mnt_table_next_fs(tb, itr, &fs) == 0) {
+               if (!mnt_fs_is_kernel(fs)) {
+                       mnt_free_iter(itr);
+                       return 0;
+               }
+       }
+
+       mnt_free_iter(itr);
+       return 1;
+}
 
 /* filter function for libmount (mnt_table_find_next_fs()) */
 static int match_func(struct libmnt_fs *fs,
@@ -886,11 +947,17 @@ static int match_func(struct libmnt_fs *fs,
                        return rc;
        }
 
+       if ((flags & FL_REAL) && mnt_fs_is_pseudofs(fs))
+           return rc;
+
+       if ((flags & FL_PSEUDO) && !mnt_fs_is_pseudofs(fs))
+           return rc;
+
        return !rc;
 }
 
 /* iterate over filesystems in @tb */
-static struct libmnt_fs *get_next_fs(struct libmnt_table *tb,
+struct libmnt_fs *get_next_fs(struct libmnt_table *tb,
                                     struct libmnt_iter *itr)
 {
        struct libmnt_fs *fs = NULL;
@@ -904,7 +971,7 @@ static struct libmnt_fs *get_next_fs(struct libmnt_table *tb,
 
        } else if (is_mount_compatible_mode()) {
                /*
-                * Look up for FS in the same way how mount(8) searchs in fstab
+                * Look up for FS in the same way how mount(8) searches in fstab
                 *
                 *   findmnt -f <spec>
                 */
@@ -922,7 +989,8 @@ static struct libmnt_fs *get_next_fs(struct libmnt_table *tb,
                 *    findmnt [-l] <spec> [-O <options>] [-t <types>]
                 */
 again:
-               mnt_table_find_next_fs(tb, itr, match_func,  NULL, &fs);
+               if (mnt_table_find_next_fs(tb, itr, match_func,  NULL, &fs) != 0)
+                       fs = NULL;
 
                if (!fs &&
                    !(flags & FL_NOSWAPMATCH) &&
@@ -947,7 +1015,7 @@ again:
 static int add_matching_lines(struct libmnt_table *tb,
                              struct libscols_table *table, int direction)
 {
-       struct libmnt_iter *itr = NULL;
+       struct libmnt_iter *itr;
        struct libmnt_fs *fs;
        int nlines = 0, rc = -1;
 
@@ -1007,7 +1075,7 @@ static int poll_table(struct libmnt_table *tb, const char *tabfile,
        FILE *f = NULL;
        int rc = -1;
        struct libmnt_iter *itr = NULL;
-       struct libmnt_table *tb_new = NULL;
+       struct libmnt_table *tb_new;
        struct libmnt_tabdiff *diff = NULL;
        struct pollfd fds[1];
 
@@ -1082,7 +1150,10 @@ static int poll_table(struct libmnt_table *tb, const char *tabfile,
                }
 
                if (count) {
-                       rc = scols_print_table(table);
+                       rc = scols_table_print_range(table, NULL, NULL);
+                       if (rc == 0)
+                               fputc('\n', scols_table_get_stream(table));
+                       fflush(stdout);
                        if (rc)
                                goto done;
                }
@@ -1092,6 +1163,7 @@ static int poll_table(struct libmnt_table *tb, const char *tabfile,
                tb = tb_new;
                tb_new = tmp;
 
+               /* remove already printed lines to reduce memory usage */
                scols_table_remove_lines(table);
                mnt_reset_table(tb_new);
 
@@ -1117,23 +1189,28 @@ static int uniq_fs_target_cmp(
        return !mnt_fs_match_target(a, mnt_fs_get_target(b), cache);
 }
 
-static void __attribute__((__noreturn__)) usage(FILE *out)
+static void __attribute__((__noreturn__)) usage(void)
 {
-       int i;
+       FILE *out = stdout;
+       size_t i;
 
        fputs(USAGE_HEADER, out);
        fprintf(out, _(
        " %1$s [options]\n"
        " %1$s [options] <device> | <mountpoint>\n"
        " %1$s [options] <device> <mountpoint>\n"
-       " %1$s [options] [--source <device>] [--target <mountpoint>]\n"),
+       " %1$s [options] [--source <device>] [--target <path> | --mountpoint <dir>]\n"),
                program_invocation_short_name);
 
+       fputs(USAGE_SEPARATOR, out);
+       fputs(_("Find a (mounted) filesystem.\n"), out);
+
        fputs(USAGE_OPTIONS, out);
        fputs(_(" -s, --fstab            search in static table of filesystems\n"), out);
-       fputs(_(" -m, --mtab             search in table of mounted filesystems\n"), out);
+       fputs(_(" -m, --mtab             search in table of mounted filesystems\n"
+               "                          (includes user space mount options)\n"), out);
        fputs(_(" -k, --kernel           search in kernel table of mounted\n"
-              "                          filesystems (default)\n"), out);
+               "                          filesystems (default)\n"), out);
        fputc('\n', out);
        fputs(_(" -p, --poll[=<list>]    monitor changes in table of mounted filesystems\n"), out);
        fputs(_(" -w, --timeout <num>    upper limit in milliseconds that --poll will block\n"), out);
@@ -1142,6 +1219,7 @@ static void __attribute__((__noreturn__)) usage(FILE *out)
        fputs(_(" -A, --all              disable all built-in filters, print all filesystems\n"), out);
        fputs(_(" -a, --ascii            use ASCII chars for tree formatting\n"), out);
        fputs(_(" -b, --bytes            print sizes in bytes rather than in human readable format\n"), out);
+       fputs(_(" -C, --nocanonicalize   don't canonicalize when comparing paths\n"), out);
        fputs(_(" -c, --canonicalize     canonicalize printed paths\n"), out);
        fputs(_(" -D, --df               imitate the output of df(1)\n"), out);
        fputs(_(" -d, --direction <word> direction of search, 'forward' or 'backward'\n"), out);
@@ -1150,34 +1228,42 @@ static void __attribute__((__noreturn__)) usage(FILE *out)
        fputs(_(" -F, --tab-file <path>  alternative file for -s, -m or -k options\n"), out);
        fputs(_(" -f, --first-only       print the first found filesystem only\n"), out);
        fputs(_(" -i, --invert           invert the sense of matching\n"), out);
+       fputs(_(" -J, --json             use JSON output format\n"), out);
        fputs(_(" -l, --list             use list format output\n"), out);
        fputs(_(" -N, --task <tid>       use alternative namespace (/proc/<tid>/mountinfo file)\n"), out);
        fputs(_(" -n, --noheadings       don't print column headings\n"), out);
-       fputs(_(" -U, --uniq             ignore filesystems with duplicate target\n"), out);
-       fputs(_(" -u, --notruncate       don't truncate text in columns\n"), out);
        fputs(_(" -O, --options <list>   limit the set of filesystems by mount options\n"), out);
        fputs(_(" -o, --output <list>    the output columns to be shown\n"), out);
+       fputs(_("     --output-all       output all available columns\n"), out);
        fputs(_(" -P, --pairs            use key=\"value\" output format\n"), out);
-       fputs(_(" -r, --raw              use raw output format\n"), out);
-       fputs(_(" -t, --types <list>     limit the set of filesystems by FS types\n"), out);
-       fputs(_(" -v, --nofsroot         don't print [/dir] for bind or btrfs mounts\n"), out);
+       fputs(_("     --pseudo           print only pseudo-filesystems\n"), out);
        fputs(_(" -R, --submounts        print all submounts for the matching filesystems\n"), out);
+       fputs(_(" -r, --raw              use raw output format\n"), out);
+       fputs(_("     --real             print only real filesystems\n"), out);
        fputs(_(" -S, --source <string>  the device to mount (by name, maj:min, \n"
                "                          LABEL=, UUID=, PARTUUID=, PARTLABEL=)\n"), out);
-       fputs(_(" -T, --target <string>  the mountpoint to use\n"), out);
+       fputs(_(" -T, --target <path>    the path to the filesystem to use\n"), out);
+       fputs(_("     --tree             enable tree format output is possible\n"), out);
+       fputs(_(" -M, --mountpoint <dir> the mountpoint directory\n"), out);
+       fputs(_(" -t, --types <list>     limit the set of filesystems by FS types\n"), out);
+       fputs(_(" -U, --uniq             ignore filesystems with duplicate target\n"), out);
+       fputs(_(" -u, --notruncate       don't truncate text in columns\n"), out);
+       fputs(_(" -v, --nofsroot         don't print [/dir] for bind or btrfs mounts\n"), out);
 
-       fputs(USAGE_SEPARATOR, out);
-       fputs(USAGE_HELP, out);
-       fputs(USAGE_VERSION, out);
+       fputc('\n', out);
+       fputs(_(" -x, --verify           verify mount table content (default is fstab)\n"), out);
+       fputs(_("     --verbose          print more details\n"), out);
 
-       fprintf(out, _("\nAvailable columns:\n"));
+       fputs(USAGE_SEPARATOR, out);
+       printf(USAGE_HELP_OPTIONS(24));
 
-       for (i = 0; i < FINDMNT_NCOLUMNS; i++)
+       fputs(USAGE_COLUMNS, out);
+       for (i = 0; i < ARRAY_SIZE(infos); i++)
                fprintf(out, " %11s  %s\n", infos[i].name, _(infos[i].help));
 
-       fprintf(out, USAGE_MAN_TAIL("findmnt(8)"));
+       printf(USAGE_MAN_TAIL("findmnt(8)"));
 
-       exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+       exit(EXIT_SUCCESS);
 }
 
 int main(int argc, char *argv[])
@@ -1185,68 +1271,91 @@ int main(int argc, char *argv[])
        struct libmnt_table *tb = NULL;
        char **tabfiles = NULL;
        int direction = MNT_ITER_FORWARD;
-       int i, c, rc = -1, timeout = -1;
+       int verify = 0;
+       int c, rc = -1, timeout = -1;
        int ntabfiles = 0, tabtype = 0;
        char *outarg = NULL;
+       size_t i;
+       int force_tree = 0, istree = 0;
 
        struct libscols_table *table = NULL;
 
+       enum {
+               FINDMNT_OPT_VERBOSE = CHAR_MAX + 1,
+               FINDMNT_OPT_TREE,
+               FINDMNT_OPT_OUTPUT_ALL,
+               FINDMNT_OPT_PSEUDO,
+               FINDMNT_OPT_REAL
+       };
+
        static const struct option longopts[] = {
-           { "all",          0, 0, 'A' },
-           { "ascii",        0, 0, 'a' },
-           { "bytes",        0, 0, 'b' },
-           { "canonicalize", 0, 0, 'c' },
-           { "direction",    1, 0, 'd' },
-           { "df",           0, 0, 'D' },
-           { "evaluate",     0, 0, 'e' },
-           { "first-only",   0, 0, 'f' },
-           { "fstab",        0, 0, 's' },
-           { "help",         0, 0, 'h' },
-           { "invert",       0, 0, 'i' },
-           { "kernel",       0, 0, 'k' },
-           { "list",         0, 0, 'l' },
-           { "mtab",         0, 0, 'm' },
-           { "noheadings",   0, 0, 'n' },
-           { "notruncate",   0, 0, 'u' },
-           { "options",      1, 0, 'O' },
-           { "output",       1, 0, 'o' },
-           { "poll",         2, 0, 'p' },
-           { "pairs",        0, 0, 'P' },
-           { "raw",          0, 0, 'r' },
-           { "types",        1, 0, 't' },
-           { "nofsroot",     0, 0, 'v' },
-           { "submounts",    0, 0, 'R' },
-           { "source",       1, 0, 'S' },
-           { "tab-file",     1, 0, 'F' },
-           { "task",         1, 0, 'N' },
-           { "target",       1, 0, 'T' },
-           { "timeout",      1, 0, 'w' },
-           { "uniq",         0, 0, 'U' },
-           { "version",      0, 0, 'V' },
-
-           { NULL,           0, 0, 0 }
+               { "all",            no_argument,       NULL, 'A'                 },
+               { "ascii",          no_argument,       NULL, 'a'                 },
+               { "bytes",          no_argument,       NULL, 'b'                 },
+               { "canonicalize",   no_argument,       NULL, 'c'                 },
+               { "direction",      required_argument, NULL, 'd'                 },
+               { "df",             no_argument,       NULL, 'D'                 },
+               { "evaluate",       no_argument,       NULL, 'e'                 },
+               { "first-only",     no_argument,       NULL, 'f'                 },
+               { "fstab",          no_argument,       NULL, 's'                 },
+               { "help",           no_argument,       NULL, 'h'                 },
+               { "invert",         no_argument,       NULL, 'i'                 },
+               { "json",           no_argument,       NULL, 'J'                 },
+               { "kernel",         no_argument,       NULL, 'k'                 },
+               { "list",           no_argument,       NULL, 'l'                 },
+               { "mountpoint",     required_argument, NULL, 'M'                 },
+               { "mtab",           no_argument,       NULL, 'm'                 },
+               { "noheadings",     no_argument,       NULL, 'n'                 },
+               { "notruncate",     no_argument,       NULL, 'u'                 },
+               { "options",        required_argument, NULL, 'O'                 },
+               { "output",         required_argument, NULL, 'o'                 },
+               { "output-all",     no_argument,       NULL, FINDMNT_OPT_OUTPUT_ALL },
+               { "poll",           optional_argument, NULL, 'p'                 },
+               { "pairs",          no_argument,       NULL, 'P'                 },
+               { "raw",            no_argument,       NULL, 'r'                 },
+               { "types",          required_argument, NULL, 't'                 },
+               { "nocanonicalize", no_argument,       NULL, 'C'                 },
+               { "nofsroot",       no_argument,       NULL, 'v'                 },
+               { "submounts",      no_argument,       NULL, 'R'                 },
+               { "source",         required_argument, NULL, 'S'                 },
+               { "tab-file",       required_argument, NULL, 'F'                 },
+               { "task",           required_argument, NULL, 'N'                 },
+               { "target",         required_argument, NULL, 'T'                 },
+               { "timeout",        required_argument, NULL, 'w'                 },
+               { "uniq",           no_argument,       NULL, 'U'                 },
+               { "verify",         no_argument,       NULL, 'x'                 },
+               { "version",        no_argument,       NULL, 'V'                 },
+               { "verbose",        no_argument,       NULL, FINDMNT_OPT_VERBOSE },
+               { "tree",           no_argument,       NULL, FINDMNT_OPT_TREE    },
+               { "real",           no_argument,       NULL, FINDMNT_OPT_REAL    },
+               { "pseudo",         no_argument,       NULL, FINDMNT_OPT_PSEUDO  },
+               { NULL, 0, NULL, 0 }
        };
 
-       static const ul_excl_t excl[] = {       /* rows and cols in in ASCII order */
+       static const ul_excl_t excl[] = {       /* rows and cols in ASCII order */
+               { 'C', 'c'},                    /* [no]canonicalize */
+               { 'C', 'e' },                   /* nocanonicalize, evaluate */
+               { 'J', 'P', 'r','x' },          /* json,pairs,raw,verify */
+               { 'M', 'T' },                   /* mountpoint, target */
                { 'N','k','m','s' },            /* task,kernel,mtab,fstab */
-               { 'P','l','r' },                /* pairs,list,raw */
+               { 'P','l','r','x' },            /* pairs,list,raw,verify */
+               { 'p','x' },                    /* poll,verify */
                { 'm','p','s' },                /* mtab,poll,fstab */
+               { FINDMNT_OPT_PSEUDO, FINDMNT_OPT_REAL },
                { 0 }
        };
        int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
 
-       assert(ARRAY_SIZE(columns) == FINDMNT_NCOLUMNS);
-
        setlocale(LC_ALL, "");
        bindtextdomain(PACKAGE, LOCALEDIR);
        textdomain(PACKAGE);
-       atexit(close_stdout);
+       close_stdout_atexit();
 
        /* default output format */
        flags |= FL_TREE;
 
        while ((c = getopt_long(argc, argv,
-                               "AabcDd:ehifF:o:O:p::PklmnN:rst:uvRS:T:Uw:V",
+                               "AabCcDd:ehiJfF:o:O:p::PklmM:nN:rst:uvRS:T:Uw:Vx",
                                longopts, NULL)) != -1) {
 
                err_exclusive_options(c, longopts, excl, excl_st);
@@ -1261,6 +1370,9 @@ int main(int argc, char *argv[])
                case 'b':
                        flags |= FL_BYTES;
                        break;
+               case 'C':
+                       flags |= FL_NOCACHE;
+                       break;
                case 'c':
                        flags |= FL_CANONICALIZE;
                        break;
@@ -1280,12 +1392,12 @@ int main(int argc, char *argv[])
                case 'e':
                        flags |= FL_EVALUATE;
                        break;
-               case 'h':
-                       usage(stdout);
-                       break;
                case 'i':
                        flags |= FL_INVERT;
                        break;
+               case 'J':
+                       flags |= FL_JSON;
+                       break;
                case 'f':
                        flags |= FL_FIRSTONLY;
                        break;
@@ -1298,6 +1410,13 @@ int main(int argc, char *argv[])
                case 'o':
                        outarg = optarg;
                        break;
+               case FINDMNT_OPT_OUTPUT_ALL:
+                       for (ncolumns = 0; ncolumns < ARRAY_SIZE(infos); ncolumns++) {
+                               if (is_tabdiff_column(ncolumns))
+                                       continue;
+                               columns[ncolumns] = ncolumns;
+                       }
+                       break;
                case 'O':
                        set_match(COL_OPTIONS, optarg);
                        break;
@@ -1356,6 +1475,9 @@ int main(int argc, char *argv[])
                        set_source_match(optarg);
                        flags |= FL_NOSWAPMATCH;
                        break;
+               case 'M':
+                       flags |= FL_STRICTTARGET;
+                       /* fallthrough */
                case 'T':
                        set_match(COL_TARGET, optarg);
                        flags |= FL_NOSWAPMATCH;
@@ -1366,34 +1488,50 @@ int main(int argc, char *argv[])
                case 'w':
                        timeout = strtos32_or_err(optarg, _("invalid timeout argument"));
                        break;
+               case 'x':
+                       verify = 1;
+                       break;
+               case FINDMNT_OPT_VERBOSE:
+                       flags |= FL_VERBOSE;
+                       break;
+               case FINDMNT_OPT_TREE:
+                       force_tree = 1;
+                       break;
+               case FINDMNT_OPT_PSEUDO:
+                       flags |= FL_PSEUDO;
+                       break;
+               case FINDMNT_OPT_REAL:
+                       flags |= FL_REAL;
+                       break;
+
+               case 'h':
+                       usage();
                case 'V':
-                       printf(UTIL_LINUX_VERSION);
-                       return EXIT_SUCCESS;
+                       print_version(EXIT_SUCCESS);
                default:
-                       usage(stderr);
-                       break;
+                       errtryhelp(EXIT_FAILURE);
                }
        }
 
        if (!ncolumns && (flags & FL_DF)) {
-               columns[ncolumns++] = COL_SOURCE;
-               columns[ncolumns++] = COL_FSTYPE;
-               columns[ncolumns++] = COL_SIZE;
-               columns[ncolumns++] = COL_USED;
-               columns[ncolumns++] = COL_AVAIL;
-               columns[ncolumns++] = COL_USEPERC;
-               columns[ncolumns++] = COL_TARGET;
+               add_column(columns, ncolumns++, COL_SOURCE);
+               add_column(columns, ncolumns++, COL_FSTYPE);
+               add_column(columns, ncolumns++, COL_SIZE);
+               add_column(columns, ncolumns++, COL_USED);
+               add_column(columns, ncolumns++, COL_AVAIL);
+               add_column(columns, ncolumns++, COL_USEPERC);
+               add_column(columns, ncolumns++, COL_TARGET);
        }
 
        /* default columns */
        if (!ncolumns) {
                if (flags & FL_POLL)
-                       columns[ncolumns++] = COL_ACTION;
+                       add_column(columns, ncolumns++, COL_ACTION);
 
-               columns[ncolumns++] = COL_TARGET;
-               columns[ncolumns++] = COL_SOURCE;
-               columns[ncolumns++] = COL_FSTYPE;
-               columns[ncolumns++] = COL_OPTIONS;
+               add_column(columns, ncolumns++, COL_TARGET);
+               add_column(columns, ncolumns++, COL_SOURCE);
+               add_column(columns, ncolumns++, COL_FSTYPE);
+               add_column(columns, ncolumns++, COL_OPTIONS);
        }
 
        if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
@@ -1401,7 +1539,7 @@ int main(int argc, char *argv[])
                return EXIT_FAILURE;
 
        if (!tabtype)
-               tabtype = TABTYPE_KERNEL;
+               tabtype = verify ? TABTYPE_FSTAB : TABTYPE_KERNEL;
 
        if ((flags & FL_POLL) && ntabfiles > 1)
                errx(EXIT_FAILURE, _("--poll accepts only one file, but more specified by --tab-file"));
@@ -1448,33 +1586,56 @@ int main(int argc, char *argv[])
        if (!tb)
                goto leave;
 
-       if ((flags & FL_TREE) && (ntabfiles > 1 || !tab_is_tree(tb)))
+       if (tabtype == TABTYPE_MTAB && tab_is_kernel(tb))
+               tabtype = TABTYPE_KERNEL;
+
+       istree = tab_is_tree(tb);
+       if (istree && force_tree)
+               flags |= FL_TREE;
+
+       if ((flags & FL_TREE) && (ntabfiles > 1 || !istree))
                flags &= ~FL_TREE;
 
-       cache = mnt_new_cache();
-       if (!cache) {
-               warn(_("failed to initialize libmount cache"));
-               goto leave;
+       if (!(flags & FL_NOCACHE)) {
+               cache = mnt_new_cache();
+               if (!cache) {
+                       warn(_("failed to initialize libmount cache"));
+                       goto leave;
+               }
+               mnt_table_set_cache(tb, cache);
+
+               if (tabtype != TABTYPE_KERNEL)
+                       cache_set_targets(cache);
        }
-       mnt_table_set_cache(tb, cache);
 
        if (flags & FL_UNIQ)
                mnt_table_uniq_fs(tb, MNT_UNIQ_KEEPTREE, uniq_fs_target_cmp);
 
+       if (verify) {
+               rc = verify_table(tb);
+               goto leave;
+       }
+
        /*
-        * initialize output formatting (libsmartcols.h)
+        * initialize libsmartcols
         */
+       scols_init_debug(0);
        table = scols_new_table();
        if (!table) {
-               warn(_("failed to initialize output table"));
+               warn(_("failed to allocate output table"));
                goto leave;
        }
        scols_table_enable_raw(table,        !!(flags & FL_RAW));
        scols_table_enable_export(table,     !!(flags & FL_EXPORT));
+       scols_table_enable_json(table,       !!(flags & FL_JSON));
        scols_table_enable_ascii(table,      !!(flags & FL_ASCII));
        scols_table_enable_noheadings(table, !!(flags & FL_NOHEADINGS));
 
+       if (flags & FL_JSON)
+               scols_table_set_name(table, "filesystems");
+
        for (i = 0; i < ncolumns; i++) {
+               struct libscols_column *cl;
                int fl = get_column_flags(i);
                int id = get_column_id(i);
 
@@ -1486,11 +1647,32 @@ int main(int argc, char *argv[])
                               "is not enabled"), get_column_name(i));
                        goto leave;
                }
-               if (!scols_table_new_column(table, get_column_name(i),
-                                       get_column_whint(i), fl)) {
-                       warn(_("failed to initialize output column"));
+               cl = scols_table_new_column(table, get_column_name(i),
+                                       get_column_whint(i), fl);
+               if (!cl)        {
+                       warn(_("failed to allocate output column"));
                        goto leave;
                }
+
+               if (flags & FL_JSON) {
+                       switch (id) {
+                       case COL_SIZE:
+                       case COL_AVAIL:
+                       case COL_USED:
+                               if (!(flags & FL_BYTES))
+                                       break;
+                               /* fallthrough */
+                       case COL_ID:
+                       case COL_FREQ:
+                       case COL_PASSNO:
+                       case COL_TID:
+                               scols_column_set_json_type(cl, SCOLS_JSON_NUMBER);
+                               break;
+                       default:
+                               scols_column_set_json_type(cl, SCOLS_JSON_STRING);
+                               break;
+                       }
+               }
        }
 
        /*
@@ -1504,19 +1686,20 @@ int main(int argc, char *argv[])
                /* whole tree */
                rc = create_treenode(table, tb, NULL, NULL);
        } else {
-               /* whole lits of sub-tree */
+               /* whole list of sub-tree */
                rc = add_matching_lines(tb, table, direction);
 
                if (rc != 0
                    && tabtype == TABTYPE_KERNEL
                    && (flags & FL_NOSWAPMATCH)
+                   && !(flags & FL_STRICTTARGET)
                    && get_match(COL_TARGET)) {
                        /*
                         * Found nothing, maybe the --target is regular file,
                         * try it again with extra functionality for target
                         * match
                         */
-                       enable_extra_target_match();
+                       enable_extra_target_match(tb);
                        rc = add_matching_lines(tb, table, direction);
                }
        }