diff --git a/misc-utils/Makemodule.am b/misc-utils/Makemodule.am index 120aadf24..a240eab66 100644 --- a/misc-utils/Makemodule.am +++ b/misc-utils/Makemodule.am @@ -143,7 +143,9 @@ 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_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..46897da96 --- /dev/null +++ b/misc-utils/findmnt-verify.c @@ -0,0 +1,201 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; + + 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)); + + 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 (mountpoint)")); + + 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, _("on boot required target unaccessible: %m")); + else + verify_warn(vfy, _("target unaccessible: %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 accessible")); + + return 0; +} + +static int verify_filesystem(struct verify_context *vfy) +{ + int rc = 0; + + if (!mnt_fs_is_swaparea(vfy->fs)) + rc = verify_target(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(); + + itr = mnt_new_iter(MNT_ITER_FORWARD); + if (!itr) { + warn(_("failed to initialize libmount iterator")); + goto done; + } + + vfy.tb = tb; + + 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, _("\nSuccess, no errors or warnings detected\n")); + + return rc != 0 ? rc : vfy.nerrors + parse_nerrors; +} 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= */ #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 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