diff options
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | libsmartcols/docs/libsmartcols-sections.txt | 2 | ||||
-rw-r--r-- | libsmartcols/src/libsmartcols.h.in | 2 | ||||
-rw-r--r-- | libsmartcols/src/libsmartcols.sym | 2 | ||||
-rw-r--r-- | libsmartcols/src/smartcolsP.h | 1 | ||||
-rw-r--r-- | libsmartcols/src/table.c | 96 | ||||
-rw-r--r-- | libsmartcols/src/table_print.c | 34 | ||||
-rw-r--r-- | misc-utils/Makemodule.am | 14 | ||||
-rw-r--r-- | misc-utils/findmnt-verify.c | 520 | ||||
-rw-r--r-- | misc-utils/findmnt.8 | 10 | ||||
-rw-r--r-- | misc-utils/findmnt.c | 81 | ||||
-rw-r--r-- | misc-utils/findmnt.h | 39 | ||||
-rw-r--r-- | sys-utils/fstab.5 | 2 | ||||
-rw-r--r-- | sys-utils/mount.8 | 4 |
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 |