summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac1
-rw-r--r--libsmartcols/docs/libsmartcols-sections.txt2
-rw-r--r--libsmartcols/src/libsmartcols.h.in2
-rw-r--r--libsmartcols/src/libsmartcols.sym2
-rw-r--r--libsmartcols/src/smartcolsP.h1
-rw-r--r--libsmartcols/src/table.c96
-rw-r--r--libsmartcols/src/table_print.c34
-rw-r--r--misc-utils/Makemodule.am14
-rw-r--r--misc-utils/findmnt-verify.c520
-rw-r--r--misc-utils/findmnt.810
-rw-r--r--misc-utils/findmnt.c81
-rw-r--r--misc-utils/findmnt.h39
-rw-r--r--sys-utils/fstab.52
-rw-r--r--sys-utils/mount.84
14 files changed, 729 insertions, 79 deletions
diff --git a/configure.ac b/configure.ac
index c0b9598ce..680f5b687 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1131,6 +1131,7 @@ AM_CONDITIONAL([BUILD_WIPEFS], [test "x$build_wipefs" = xyes])
UL_BUILD_INIT([findmnt], [check])
UL_REQUIRES_BUILD([findmnt], [libmount])
+UL_REQUIRES_BUILD([findmnt], [libblkid])
UL_REQUIRES_BUILD([findmnt], [libsmartcols])
AM_CONDITIONAL([BUILD_FINDMNT], [test "x$build_findmnt" = xyes])
diff --git a/libsmartcols/docs/libsmartcols-sections.txt b/libsmartcols/docs/libsmartcols-sections.txt
index 324f6e15f..ccda20196 100644
--- a/libsmartcols/docs/libsmartcols-sections.txt
+++ b/libsmartcols/docs/libsmartcols-sections.txt
@@ -116,6 +116,7 @@ scols_table_get_line_separator
scols_table_get_ncols
scols_table_get_nlines
scols_table_get_stream
+scols_table_get_symbols
scols_table_get_termforce
scols_table_get_termwidth
scols_table_get_title
@@ -137,6 +138,7 @@ scols_table_remove_columns
scols_table_remove_line
scols_table_remove_lines
scols_table_set_column_separator
+scols_table_set_default_symbols
scols_table_set_line_separator
scols_table_set_name
scols_table_set_stream
diff --git a/libsmartcols/src/libsmartcols.h.in b/libsmartcols/src/libsmartcols.h.in
index 0fa9b464d..22cab64a1 100644
--- a/libsmartcols/src/libsmartcols.h.in
+++ b/libsmartcols/src/libsmartcols.h.in
@@ -242,6 +242,8 @@ extern struct libscols_line *scols_table_new_line(struct libscols_table *tb, str
extern struct libscols_line *scols_table_get_line(struct libscols_table *tb, size_t n);
extern struct libscols_table *scols_copy_table(struct libscols_table *tb);
extern int scols_table_set_symbols(struct libscols_table *tb, struct libscols_symbols *sy);
+extern int scols_table_set_default_symbols(struct libscols_table *tb);
+extern struct libscols_symbols *scols_table_get_symbols(const struct libscols_table *tb);
extern int scols_table_set_stream(struct libscols_table *tb, FILE *stream);
extern FILE *scols_table_get_stream(const struct libscols_table *tb);
diff --git a/libsmartcols/src/libsmartcols.sym b/libsmartcols/src/libsmartcols.sym
index 774d84fe0..aca648b63 100644
--- a/libsmartcols/src/libsmartcols.sym
+++ b/libsmartcols/src/libsmartcols.sym
@@ -141,8 +141,10 @@ SMARTCOLS_2.29 {
global:
scols_column_is_wrapnl;
scols_symbols_set_cell_padding;
+ scols_table_get_symbols;
scols_table_get_termforce;
scols_table_get_termwidth;
+ scols_table_set_default_symbols;
scols_table_set_termforce;
scols_table_set_termwidth;
scols_table_get_name;
diff --git a/libsmartcols/src/smartcolsP.h b/libsmartcols/src/smartcolsP.h
index ca6ea1be7..8e8bd768e 100644
--- a/libsmartcols/src/smartcolsP.h
+++ b/libsmartcols/src/smartcolsP.h
@@ -161,6 +161,7 @@ struct libscols_table {
padding_debug :1, /* output visible padding chars */
maxout :1, /* maximize output */
header_printed :1, /* header already printed */
+ priv_symbols :1, /* default private symbols */
no_headings :1, /* don't print header */
no_linesep :1, /* don't print line separator */
no_wrap :1; /* never wrap lines */
diff --git a/libsmartcols/src/table.c b/libsmartcols/src/table.c
index 3b0424704..759b54c27 100644
--- a/libsmartcols/src/table.c
+++ b/libsmartcols/src/table.c
@@ -680,14 +680,62 @@ err:
}
/**
+ * scols_table_set_default_symbols:
+ * @tb: table
+ *
+ * The library check the current environment to select ASCII or UTF8 symbols.
+ * This default behavior could be controlled by scols_table_enable_ascii().
+ *
+ * Use scols_table_set_symbols() to unset symbols or use your own setting.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_table_set_default_symbols(struct libscols_table *tb)
+{
+ struct libscols_symbols *sy;
+ int rc;
+
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "setting default symbols"));
+
+ sy = scols_new_symbols();
+ if (!sy)
+ return -ENOMEM;
+
+#if defined(HAVE_WIDECHAR)
+ if (!scols_table_is_ascii(tb) &&
+ !strcmp(nl_langinfo(CODESET), "UTF-8")) {
+ scols_symbols_set_branch(sy, UTF_VR UTF_H);
+ scols_symbols_set_vertical(sy, UTF_V " ");
+ scols_symbols_set_right(sy, UTF_UR UTF_H);
+ } else
+#endif
+ {
+ scols_symbols_set_branch(sy, "|-");
+ scols_symbols_set_vertical(sy, "| ");
+ scols_symbols_set_right(sy, "`-");
+ }
+ scols_symbols_set_title_padding(sy, " ");
+ scols_symbols_set_cell_padding(sy, " ");
+
+ rc = scols_table_set_symbols(tb, sy);
+ scols_unref_symbols(sy);
+ return rc;
+}
+
+
+/**
* scols_table_set_symbols:
* @tb: table
* @sy: symbols or NULL
*
* Add a reference to @sy from the table. The symbols are used by library to
- * draw tree output. If no symbols are specified then library checks the
- * current environment to select ASCII or UTF8 symbols. This default behavior
- * could be controlled by scols_table_enable_ascii().
+ * draw tree output. If no symbols are used for the table then library creates
+ * default temporary symbols to draw output by scols_table_set_default_symbols().
+ *
+ * If @sy is NULL then remove reference from the currenly uses symbols.
*
* Returns: 0, a negative value in case of an error.
*/
@@ -697,38 +745,34 @@ int scols_table_set_symbols(struct libscols_table *tb,
if (!tb)
return -EINVAL;
- DBG(TAB, ul_debugobj(tb, "setting alternative symbols %p", sy));
-
- if (tb->symbols) /* unref old */
+ /* remove old */
+ if (tb->symbols) {
+ DBG(TAB, ul_debugobj(tb, "remove symbols %p refrence", tb->symbols));
scols_unref_symbols(tb->symbols);
+ tb->symbols = NULL;
+ }
+
+ /* set new */
if (sy) { /* ref user defined */
+ DBG(TAB, ul_debugobj(tb, "set symbols so %p", sy));
tb->symbols = sy;
scols_ref_symbols(sy);
- } else { /* default symbols */
- tb->symbols = scols_new_symbols();
- if (!tb->symbols)
- return -ENOMEM;
-#if defined(HAVE_WIDECHAR)
- if (!scols_table_is_ascii(tb) &&
- !strcmp(nl_langinfo(CODESET), "UTF-8")) {
- scols_symbols_set_branch(tb->symbols, UTF_VR UTF_H);
- scols_symbols_set_vertical(tb->symbols, UTF_V " ");
- scols_symbols_set_right(tb->symbols, UTF_UR UTF_H);
- } else
-#endif
- {
- scols_symbols_set_branch(tb->symbols, "|-");
- scols_symbols_set_vertical(tb->symbols, "| ");
- scols_symbols_set_right(tb->symbols, "`-");
- }
- scols_symbols_set_title_padding(tb->symbols, " ");
- scols_symbols_set_cell_padding(tb->symbols, " ");
}
-
return 0;
}
/**
+ * scols_table_get_symbols:
+ * @tb: table
+ *
+ * Returns: pointer to symbols table.
+ */
+struct libscols_symbols *scols_table_get_symbols(const struct libscols_table *tb)
+{
+ return tb->symbols;
+}
+
+/**
* scols_table_enable_nolinesep:
* @tb: table
* @enable: 1 or 0
diff --git a/libsmartcols/src/table_print.c b/libsmartcols/src/table_print.c
index 7612682cd..5771111b3 100644
--- a/libsmartcols/src/table_print.c
+++ b/libsmartcols/src/table_print.c
@@ -1339,6 +1339,19 @@ static size_t strlen_line(struct libscols_line *ln)
return sz;
}
+static void cleanup_printing(struct libscols_table *tb, struct libscols_buffer *buf)
+{
+ if (!tb)
+ return;
+
+ free_buffer(buf);
+
+ if (tb->priv_symbols) {
+ scols_table_set_symbols(tb, NULL);
+ tb->priv_symbols = 0;
+ }
+}
+
static int initialize_printing(struct libscols_table *tb, struct libscols_buffer **buf)
{
size_t bufsz, extra_bufsz = 0;
@@ -1348,8 +1361,11 @@ static int initialize_printing(struct libscols_table *tb, struct libscols_buffer
DBG(TAB, ul_debugobj(tb, "initialize printing"));
- if (!tb->symbols)
- scols_table_set_symbols(tb, NULL); /* use default */
+ if (!tb->symbols) {
+ scols_table_set_default_symbols(tb);
+ tb->priv_symbols = 1;
+ } else
+ tb->priv_symbols = 0;
if (tb->format == SCOLS_FMT_HUMAN)
tb->is_term = tb->termforce == SCOLS_TERMFORCE_NEVER ? 0 :
@@ -1414,8 +1430,10 @@ static int initialize_printing(struct libscols_table *tb, struct libscols_buffer
}
*buf = new_buffer(bufsz + 1); /* data + space for \0 */
- if (!*buf)
- return -ENOMEM;
+ if (!*buf) {
+ rc = -ENOMEM;
+ goto err;
+ }
if (tb->format == SCOLS_FMT_HUMAN) {
rc = recount_widths(tb, *buf);
@@ -1425,7 +1443,7 @@ static int initialize_printing(struct libscols_table *tb, struct libscols_buffer
return 0;
err:
- free_buffer(*buf);
+ cleanup_printing(tb, *buf);
return rc;
}
@@ -1436,7 +1454,7 @@ err:
* @end: last printed line or NULL to print all from start.
*
* If the start is the first line in the table than prints table header too.
- * The header is printed only once.
+ * The header is printed only once. This does not work for trees.
*
* Returns: 0, a negative value in case of an error.
*/
@@ -1472,7 +1490,7 @@ int scols_table_print_range( struct libscols_table *tb,
rc = print_range(tb, buf, &itr, end);
done:
- free_buffer(buf);
+ cleanup_printing(tb, buf);
return rc;
}
@@ -1564,7 +1582,7 @@ static int __scols_print_table(struct libscols_table *tb, int *is_empty)
fput_table_close(tb);
done:
- free_buffer(buf);
+ cleanup_printing(tb, buf);
return rc;
}
diff --git a/misc-utils/Makemodule.am b/misc-utils/Makemodule.am
index 120aadf24..ce9df2c32 100644
--- a/misc-utils/Makemodule.am
+++ b/misc-utils/Makemodule.am
@@ -141,9 +141,17 @@ endif
if BUILD_FINDMNT
bin_PROGRAMS += findmnt
dist_man_MANS += misc-utils/findmnt.8
-findmnt_LDADD = $(LDADD) libmount.la libcommon.la libsmartcols.la
-findmnt_CFLAGS = $(AM_CFLAGS) -I$(ul_libmount_incdir) -I$(ul_libsmartcols_incdir)
-findmnt_SOURCES = misc-utils/findmnt.c
+findmnt_LDADD = $(LDADD) libmount.la \
+ libcommon.la \
+ libsmartcols.la \
+ libblkid.la
+findmnt_CFLAGS = $(AM_CFLAGS) \
+ -I$(ul_libmount_incdir) \
+ -I$(ul_libsmartcols_incdir) \
+ -I$(ul_libblkid_incdir)
+findmnt_SOURCES = misc-utils/findmnt.c \
+ misc-utils/findmnt-verify.c \
+ misc-utils/findmnt.h
if HAVE_UDEV
findmnt_LDADD += -ludev
endif
diff --git a/misc-utils/findmnt-verify.c b/misc-utils/findmnt-verify.c
new file mode 100644
index 000000000..a096ddec5
--- /dev/null
+++ b/misc-utils/findmnt-verify.c
@@ -0,0 +1,520 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <libmount.h>
+#include <blkid.h>
+#include <sys/utsname.h>
+
+#include "nls.h"
+#include "c.h"
+#include "strutils.h"
+#include "xalloc.h"
+
+#include "findmnt.h"
+
+struct verify_context {
+ struct libmnt_fs *fs;
+ struct libmnt_table *tb;
+
+ char **fs_ary;
+ size_t fs_num;
+ size_t fs_alloc;
+
+ int nwarnings;
+ int nerrors;
+
+ unsigned int target_printed : 1;
+};
+
+static void verify_mesg(struct verify_context *vfy, char type, const char *fmt, va_list ap)
+{
+ if (!vfy->target_printed) {
+ fprintf(stdout, "%s\n", mnt_fs_get_target(vfy->fs));
+ vfy->target_printed = 1;
+ }
+
+ fprintf(stdout, " [%c] ", type);
+ vfprintf(stdout, fmt, ap);
+ fputc('\n', stdout);
+}
+
+static int verify_warn(struct verify_context *vfy, const char *fmt, ...)
+{
+ va_list ap;
+ vfy->nwarnings++;
+ va_start(ap, fmt);
+ verify_mesg(vfy, 'W', fmt, ap);
+ va_end(ap);
+ return 0;
+}
+
+static int verify_err(struct verify_context *vfy, const char *fmt, ...)
+{
+ va_list ap;
+ vfy->nerrors++;
+ va_start(ap, fmt);
+ verify_mesg(vfy, 'E', fmt, ap);
+ va_end(ap);
+ return 0;
+}
+
+static int verify_ok(struct verify_context *vfy __attribute__((unused)),
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!(flags & FL_VERBOSE))
+ return 0;
+
+ va_start(ap, fmt);
+ verify_mesg(vfy, ' ', fmt, ap);
+ va_end(ap);
+ return 0;
+}
+
+static int verify_order(struct verify_context *vfy)
+{
+ struct libmnt_iter *itr = NULL;
+ struct libmnt_fs *next;
+ const char *tgt;
+
+ tgt = mnt_fs_get_target(vfy->fs);
+ if (tgt && !(flags & FL_NOCACHE))
+ tgt = mnt_resolve_target(tgt, cache);
+ else if (!tgt)
+ return 0;
+
+ itr = mnt_new_iter(MNT_ITER_FORWARD);
+ if (!itr) {
+ warn(_("failed to initialize libmount iterator"));
+ goto done;
+ }
+
+ /* set iterator position to 'fs' */
+ mnt_table_set_iter(vfy->tb, itr, vfy->fs);
+ mnt_table_next_fs(vfy->tb, itr, &next);
+
+ /* scan all next filesystems */
+ while (mnt_table_next_fs(vfy->tb, itr, &next) == 0) {
+ const char *n_tgt;
+ size_t len;
+
+ n_tgt = mnt_fs_get_target(next);
+ if (n_tgt && !(flags & FL_NOCACHE))
+ n_tgt = mnt_resolve_target(n_tgt, cache);
+ else if (!n_tgt)
+ continue;
+ len = strlen(n_tgt);
+
+ if (strncmp(n_tgt, tgt, len) == 0) {
+ if (*(tgt + len) == '\0')
+ verify_warn(vfy, _("target specified more than once"));
+ else if (*(tgt + len) == '/')
+ verify_err(vfy, _("wrong order: %s specified before %s"), tgt, n_tgt);
+ }
+ }
+done:
+ mnt_free_iter(itr);
+ return 0;
+}
+
+static int verify_target(struct verify_context *vfy)
+{
+ const char *tgt = mnt_fs_get_target(vfy->fs);
+ const char *cn = tgt;
+ struct stat sb;
+
+ if (!tgt)
+ return verify_err(vfy, _("undefined target (fs_file)"));
+
+ if (!(flags & FL_NOCACHE)) {
+ cn = mnt_resolve_target(tgt, cache);
+ if (!cn)
+ return -ENOMEM;
+ if (strcmp(cn, tgt) != 0)
+ verify_warn(vfy, _("non-canonical target path (real: %s)"), cn);
+ tgt = cn;
+ }
+ if (stat(tgt, &sb) != 0) {
+ if (mnt_fs_get_option(vfy->fs, "noauto", NULL, NULL) == 1)
+ verify_err(vfy, _("unreachable on boot required target: %m"));
+ else
+ verify_warn(vfy, _("unreachable target: %m"));
+
+ } else if (!S_ISDIR(sb.st_mode)
+ && mnt_fs_get_option(vfy->fs, "bind", NULL, NULL) == 1) {
+ verify_err(vfy, _("target is not a directory"));
+ } else
+ verify_ok(vfy, _("target exists"));
+
+ return 0;
+}
+
+static char *verify_tag(struct verify_context *vfy, const char *name,
+ const char *value)
+{
+ char *src = mnt_resolve_tag(name, value, cache);
+
+ if (!src) {
+ if (mnt_fs_get_option(vfy->fs, "noauto", NULL, NULL) == 1)
+ verify_err(vfy, _("unreachable on boot required source: %s=%s"), name, value);
+ else
+ verify_warn(vfy, _("unreachable: %s=%s"), name, value);
+ } else
+ verify_ok(vfy, _("%s=%s translated to %s"), name, value, src);
+
+ return src;
+}
+
+/* Note that mount source is very FS specific and we should not
+ * interpret unreachable source as error. The exception is only
+ * NAME=value, this has to be convertible to device name.
+ */
+static int verify_source(struct verify_context *vfy)
+{
+ const char *src = mnt_fs_get_srcpath(vfy->fs);
+ char *t = NULL, *v = NULL;
+ struct stat sb;
+ int isbind;
+
+ /* source is NAME=value tag */
+ if (!src) {
+ const char *tag = NULL, *val = NULL;
+
+ if (mnt_fs_get_tag(vfy->fs, &tag, &val) != 0)
+ return verify_err(vfy, _("undefined source (fs_spec)"));
+
+ src = verify_tag(vfy, tag, val);
+ if (!src)
+ goto done;
+
+ /* blkid is able to parse it, but libmount does not see it as a tag --
+ * it means unsupported tag */
+ } else if (blkid_parse_tag_string(src, &t, &v) == 0 && stat(src, &sb) != 0)
+ return verify_err(vfy, _("unsupported source tag: %s"), src);
+
+ isbind = mnt_fs_get_option(vfy->fs, "bind", NULL, NULL) == 0;
+
+ /* source is path */
+ if (mnt_fs_is_pseudofs(vfy->fs) || mnt_fs_is_netfs(vfy->fs))
+ verify_ok(vfy, _("do not check %s source (pseudo/net)"), src);
+
+ else if (stat(src, &sb) != 0)
+ verify_warn(vfy, _("unreachable source: %s: %m"), src);
+
+ else if ((S_ISDIR(sb.st_mode) || S_ISREG(sb.st_mode)) && !isbind)
+ verify_warn(vfy, _("non-bind mount source %s is a directory or regular file"), src);
+
+ else if (!S_ISBLK(sb.st_mode) && !isbind)
+ verify_warn(vfy, _("source %s is not a block device"), src);
+ else
+ verify_ok(vfy, _("source %s exists"), src);
+done:
+ free(t);
+ free(v);
+ return 0;
+}
+
+static int verify_options(struct verify_context *vfy)
+{
+ const char *opts;
+
+ opts = mnt_fs_get_vfs_options(vfy->fs);
+ if (opts)
+ verify_ok(vfy, _("VFS options: %s"), opts);
+
+ opts = mnt_fs_get_fs_options(vfy->fs);
+ if (opts)
+ verify_ok(vfy, _("FS options: %s"), opts);
+
+ opts = mnt_fs_get_user_options(vfy->fs);
+ if (opts)
+ verify_ok(vfy, _("userspace options: %s"), opts);
+
+ return 0;
+}
+
+static int verify_swaparea(struct verify_context *vfy)
+{
+ char *arg;
+ size_t argsz = 0;
+
+ if (mnt_fs_get_option(vfy->fs, "discard", &arg, &argsz) == 0
+ && arg
+ && strncmp(arg, "once", argsz) != 0
+ && strncmp(arg, "pages", argsz) != 0)
+ verify_err(vfy, _("unsupported swaparea discard policy: %s"), arg);
+
+ if (mnt_fs_get_option(vfy->fs, "pri", &arg, &argsz) == 0 && arg) {
+ char *p = arg;
+ if (*p == '-')
+ p++;
+ for (; p < arg + argsz; p++) {
+ if (!isdigit((unsigned char) *p)) {
+ verify_err(vfy, _("failed to parse swaparea priority option"));
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int is_supported_filesystem(struct verify_context *vfy, const char *name)
+{
+ size_t n;
+
+ if (!vfy->fs_num)
+ return 0;
+
+ for (n = 0; n < vfy->fs_num; n++ ) {
+ if (strcmp(vfy->fs_ary[n], name) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int add_filesystem(struct verify_context *vfy, const char *name)
+{
+ #define MYCHUNK 16
+
+ if (is_supported_filesystem(vfy, name))
+ return 0;
+
+ if (vfy->fs_alloc == 0 || vfy->fs_num + 1 <= vfy->fs_alloc) {
+ vfy->fs_alloc = ((vfy->fs_alloc + 1 + MYCHUNK) / MYCHUNK) * MYCHUNK;
+ vfy->fs_ary = xrealloc(vfy->fs_ary, vfy->fs_alloc * sizeof(char *));
+ }
+
+ vfy->fs_ary[vfy->fs_num] = xstrdup(name);
+ vfy->fs_num++;
+
+ return 0;
+}
+
+static int read_proc_filesystems(struct verify_context *vfy)
+{
+ int rc = 0;
+ FILE *f;
+ char buf[80], *cp, *t;
+
+ f = fopen("/proc/filesystems", "r");
+ if (!f)
+ return -errno;
+
+ while (!feof(f)) {
+ if (!fgets(buf, sizeof(buf), f))
+ break;
+ cp = buf;
+ if (!isspace(*cp)) {
+ while (*cp && !isspace(*cp))
+ cp++;
+ }
+ while (*cp && isspace(*cp))
+ cp++;
+ if ((t = strchr(cp, '\n')) != NULL)
+ *t = 0;
+ if ((t = strchr(cp, '\t')) != NULL)
+ *t = 0;
+ if ((t = strchr(cp, ' ')) != NULL)
+ *t = 0;
+
+ rc = add_filesystem(vfy, cp);
+ if (rc)
+ return rc;
+ }
+ fclose(f);
+ return 0;
+}
+
+static int read_kernel_filesystems(struct verify_context *vfy)
+{
+#ifdef __linux__
+ struct utsname uts;
+ FILE *f;
+ char buf[1024];
+
+ if (uname(&uts))
+ return 0;
+ snprintf(buf, sizeof(buf), "/lib/modules/%s/modules.dep", uts.release);
+
+ f = fopen(buf, "r");
+ if (!f)
+ return 0;
+
+ while (!feof(f)) {
+ char *p, *name;
+ int rc;
+
+ if (!fgets(buf, sizeof(buf), f))
+ break;
+
+ if (strncmp("kernel/fs/", buf, 10) != 0 ||
+ strncmp("kernel/fs/nls/", buf, 14) == 0)
+ continue;
+
+ p = strchr(buf, ':');
+ if (!p)
+ continue;
+ *p = '\0';
+
+ name = strrchr(buf, '/');
+ if (!name)
+ continue;
+ name++;
+
+ p = strstr(name, ".ko");
+ if (!p)
+ continue;
+ *p = '\0';
+
+ rc = add_filesystem(vfy, name);
+ if (rc)
+ return rc;
+ }
+ fclose(f);
+#endif /* __linux__ */
+ return 0;
+}
+
+static int verify_fstype(struct verify_context *vfy)
+{
+ const char *src = mnt_resolve_spec(mnt_fs_get_source(vfy->fs), cache);
+ const char *type, *realtype;
+ int ambi = 0, isauto = 0, isswap = 0;
+
+ if (!src)
+ return 0;
+ if (mnt_fs_is_pseudofs(vfy->fs) || mnt_fs_is_netfs(vfy->fs))
+ return verify_ok(vfy, _("do not check %s FS type (pseudo/net)"), src);
+
+ type = mnt_fs_get_fstype(vfy->fs);
+
+ if (type) {
+ int none = strcmp(type, "none") == 0;
+
+ if (none
+ && mnt_fs_get_option(vfy->fs, "bind", NULL, NULL) == 1
+ && mnt_fs_get_option(vfy->fs, "move", NULL, NULL) == 1)
+ return verify_warn(vfy, _("\"none\" FS type is recommended for bind or move oprations only"));
+
+ else if (strcmp(type, "auto") == 0)
+ isauto = 1;
+ else if (strcmp(type, "swap") == 0)
+ isswap = 1;
+
+ if (!isswap && !isauto && !none && !is_supported_filesystem(vfy, type))
+ verify_warn(vfy, _("%s seems unspported by the current kernel"), type);
+ }
+ realtype = mnt_get_fstype(src, &ambi, cache);
+
+ if (!realtype) {
+ if (isauto)
+ return verify_err(vfy, _("cannot detect on-disk filesystem type"));
+ return verify_warn(vfy, _("cannot detect on-disk filesystem type"));
+ }
+
+ if (realtype) {
+ isswap = strcmp(realtype, "swap") == 0;
+
+ if (type && !isauto && strcmp(type, realtype) != 0)
+ return verify_err(vfy, _("%s does not match with on-disk %s"), type, realtype);
+
+ if (!isswap && !is_supported_filesystem(vfy, realtype))
+ return verify_err(vfy, _("on-disk %s seems unspported by the current kernel"), realtype);
+
+ verify_ok(vfy, _("FS type is %s"), realtype);
+ }
+
+ return 0;
+}
+
+static int verify_passno(struct verify_context *vfy)
+{
+ int passno = mnt_fs_get_passno(vfy->fs);
+ const char *tgt = mnt_fs_get_target(vfy->fs);
+
+ if (tgt && strcmp("/", tgt) == 0 && passno != 1)
+ return verify_warn(vfy, _("recommended root FS passno is 1 (current is %d)"), passno);
+
+ return 0;
+}
+
+static int verify_filesystem(struct verify_context *vfy)
+{
+ int rc = 0;
+
+ if (mnt_fs_is_swaparea(vfy->fs))
+ rc = verify_swaparea(vfy);
+ else {
+ rc = verify_target(vfy);
+ if (!rc)
+ rc = verify_options(vfy);
+ }
+
+ if (!rc)
+ rc = verify_source(vfy);
+ if (!rc)
+ rc = verify_fstype(vfy);
+ if (!rc)
+ rc = verify_passno(vfy);
+
+ return rc;
+}
+
+int verify_table(struct libmnt_table *tb)
+{
+ struct verify_context vfy = { .nerrors = 0 };
+ struct libmnt_iter *itr = NULL;
+ int rc = 0; /* overall return code (alloc errors, etc.) */
+ int check_order = is_listall_mode();
+ static int has_read_fs = 0;
+
+ itr = mnt_new_iter(MNT_ITER_FORWARD);
+ if (!itr) {
+ warn(_("failed to initialize libmount iterator"));
+ goto done;
+ }
+
+ vfy.tb = tb;
+
+ if (has_read_fs == 0) {
+ read_proc_filesystems(&vfy);
+ read_kernel_filesystems(&vfy);
+ has_read_fs = 1;
+ }
+
+ while (rc == 0 && (vfy.fs = get_next_fs(tb, itr))) {
+ vfy.target_printed = 0;
+ if (check_order)
+ rc = verify_order(&vfy);
+ if (!rc)
+ rc = verify_filesystem(&vfy);
+
+ if (flags & FL_FIRSTONLY)
+ break;
+ flags |= FL_NOSWAPMATCH;
+ }
+
+done:
+ mnt_free_iter(itr);
+
+ /* summary */
+ if (vfy.nerrors || parse_nerrors || vfy.nwarnings) {
+ fputc('\n', stderr);
+ fprintf(stderr, P_("%d parse error", "%d parse errors", parse_nerrors), parse_nerrors);
+ fprintf(stderr, P_(", %d error", ", %d errors", vfy.nerrors), vfy.nerrors);
+ fprintf(stderr, P_(", %d warning", ", %d warnings", vfy.nwarnings), vfy.nwarnings);
+ fputc('\n', stderr);
+ } else
+ fprintf(stdout, _("Success, no errors or warnings detected\n"));
+
+ return rc != 0 ? rc : vfy.nerrors + parse_nerrors;
+}
diff --git a/misc-utils/findmnt.8 b/misc-utils/findmnt.8
index fd26f5e5c..2a6ed3c24 100644
--- a/misc-utils/findmnt.8
+++ b/misc-utils/findmnt.8
@@ -233,6 +233,16 @@ Do not print a [/dir] in the SOURCE column for bind mounts or btrfs subvolumes.
.TP
.BR \-w , " \-\-timeout \fImilliseconds\fP"
Specify an upper limit on the time for which \fB\-\-poll\fR will block, in milliseconds.
+.TP
+.BR \-x , " \-\-verify"
+Check mount table content. The default is to verify
+.IR /etc/fstab
+parsability and usability. It's possible to use this option also with \fB\-\-tab\-file\fP.
+It's possible to specify source (device) or target (mountpoint) to filter mount table. The option
+\fB\-\-verbose\fP forces findmnt to print more details.
+.TP
+.BR " \-\-verbose"
+Force findmnt to print more information (\fB\-\-verify\fP only for now).
.SH EXAMPLES
.IP "\fBfindmnt \-\-fstab \-t nfs\fP"
Prints all NFS filesystems defined in
diff --git a/misc-utils/findmnt.c b/misc-utils/findmnt.c
index b2ff04e8f..ea4d23d8e 100644
--- a/misc-utils/findmnt.c
+++ b/misc-utils/findmnt.c
@@ -47,31 +47,7 @@
#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),
- FL_NOCACHE = (1 << 14),
- FL_STRICTTARGET = (1 << 15),
-
- /* basic table settings */
- FL_ASCII = (1 << 20),
- FL_RAW = (1 << 21),
- FL_NOHEADINGS = (1 << 22),
- FL_EXPORT = (1 << 23),
- FL_TREE = (1 << 24),
- FL_JSON = (1 << 25),
-};
+#include "findmnt.h"
/* column IDs */
enum {
@@ -166,17 +142,16 @@ static inline size_t err_columns_index(size_t arysz, size_t idx)
#define add_column(ary, n, id) \
((ary)[ err_columns_index(ARRAY_SIZE(ary), (n)) ] = (id))
-/* global flags */
-static int flags;
-
-
/* 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;
@@ -315,7 +290,7 @@ 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))
return 0;
@@ -805,6 +780,7 @@ static int parser_errcb(struct libmnt_table *tb __attribute__ ((__unused__)),
const char *filename, int line)
{
warnx(_("%s: parse error at line %d -- ignored"), filename, line);
+ ++parse_nerrors;
return 1;
}
@@ -973,7 +949,7 @@ static int match_func(struct libmnt_fs *fs,
}
/* 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;
@@ -1254,7 +1230,12 @@ static void __attribute__((__noreturn__)) usage(FILE *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(_(" -v, --nofsroot don't print [/dir] for bind or btrfs mounts\n"), out);
+
+ fputc('\n', out);
+ fputs(_(" -x, --verify verify mount table content (default is fstab)\n"), out);
+ fputs(_(" --verbose print more details\n"), out);
+ fputc('\n', out);
fputs(USAGE_SEPARATOR, out);
fputs(USAGE_HELP, out);
@@ -1275,6 +1256,7 @@ int main(int argc, char *argv[])
struct libmnt_table *tb = NULL;
char **tabfiles = NULL;
int direction = MNT_ITER_FORWARD;
+ int verify = 0;
int c, rc = -1, timeout = -1;
int ntabfiles = 0, tabtype = 0;
char *outarg = NULL;
@@ -1282,6 +1264,10 @@ int main(int argc, char *argv[])
struct libscols_table *table = NULL;
+ enum {
+ FINDMNT_OPT_VERBOSE = CHAR_MAX + 1
+ };
+
static const struct option longopts[] = {
{ "all", 0, 0, 'A' },
{ "ascii", 0, 0, 'a' },
@@ -1316,18 +1302,20 @@ int main(int argc, char *argv[])
{ "target", 1, 0, 'T' },
{ "timeout", 1, 0, 'w' },
{ "uniq", 0, 0, 'U' },
+ { "verify", 0, 0, 'x' },
{ "version", 0, 0, 'V' },
-
+ { "verbose", 0, 0, FINDMNT_OPT_VERBOSE },
{ NULL, 0, 0, 0 }
};
static const ul_excl_t excl[] = { /* rows and cols in in ASCII order */
{ 'C', 'c'}, /* [no]canonicalize */
{ 'C', 'e' }, /* nocanonicalize, evaluate */
- { 'J', 'P', 'r' }, /* json,pairs,raw */
+ { '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 */
{ 0 }
};
@@ -1342,7 +1330,7 @@ int main(int argc, char *argv[])
flags |= FL_TREE;
while ((c = getopt_long(argc, argv,
- "AabCcDd:ehiJfF:o:O:p::PklmM:nN: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);
@@ -1474,6 +1462,12 @@ int main(int argc, char *argv[])
case 'V':
printf(UTIL_LINUX_VERSION);
return EXIT_SUCCESS;
+ case 'x':
+ verify = 1;
+ break;
+ case FINDMNT_OPT_VERBOSE:
+ flags |= FL_VERBOSE;
+ break;
default:
usage(stderr);
break;
@@ -1506,7 +1500,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"));
@@ -1548,7 +1542,6 @@ int main(int argc, char *argv[])
* initialize libmount
*/
mnt_init_debug(0);
- scols_init_debug(0);
tb = parse_tabfiles(tabfiles, ntabfiles, tabtype);
if (!tb)
@@ -1575,9 +1568,15 @@ int main(int argc, char *argv[])
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"));
diff --git a/misc-utils/findmnt.h b/misc-utils/findmnt.h
new file mode 100644
index 000000000..fbaa38e82
--- /dev/null
+++ b/misc-utils/findmnt.h
@@ -0,0 +1,39 @@
+#ifndef UTIL_LINUX_FINDMNT_H
+#define UTIL_LINUX_FINDMNT_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),
+ FL_NOCACHE = (1 << 14),
+ FL_STRICTTARGET = (1 << 15),
+ FL_VERBOSE = (1 << 16),
+
+ /* basic table settings */
+ FL_ASCII = (1 << 20),
+ FL_RAW = (1 << 21),
+ FL_NOHEADINGS = (1 << 22),
+ FL_EXPORT = (1 << 23),
+ FL_TREE = (1 << 24),
+ FL_JSON = (1 << 25),
+};
+
+extern struct libmnt_cache *cache;
+extern int flags;
+extern int parse_nerrors;
+
+extern int is_listall_mode(void);
+extern struct libmnt_fs *get_next_fs(struct libmnt_table *tb, struct libmnt_iter *itr);
+extern int verify_table(struct libmnt_table *tb);
+
+#endif /* UTIL_LINUX_FINDMNT_H */
diff --git a/sys-utils/fstab.5 b/sys-utils/fstab.5
index 2f20fed06..bd9ce2e71 100644
--- a/sys-utils/fstab.5
+++ b/sys-utils/fstab.5
@@ -114,7 +114,7 @@ lower case characters.
.B The second field
.RI ( fs_file ).
.RS
-This field describes the mount point for the filesystem. For swap partitions, this
+This field describes the mount point (target) for the filesystem. For swap partitions, this
field should be specified as `none'. If the name of the mount point
contains spaces these can be escaped as `\\040'.
.RE
diff --git a/sys-utils/mount.8 b/sys-utils/mount.8
index 17c5eb2d8..f00af8f35 100644
--- a/sys-utils/mount.8
+++ b/sys-utils/mount.8
@@ -515,6 +515,10 @@ Mount all filesystems (of the given types) mentioned in
.B noauto
keyword). The filesystems are mounted following their order in
.IR fstab .
+.sp
+Note that it is a bad manner to use \fBmount \-a\fR for
+.IR fstab
+checking. The recommended solution is \fBfindmnt \-\-verify\fR.
.TP
.BR \-B , " \-\-bind"
Remount a subtree somewhere else (so that its contents are available