findmnt: add --verify and --verbose
Signed-off-by: Karel Zak <kzak@redhat.com>
This commit is contained in:
parent
e0140aa138
commit
c768892f4c
|
@ -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
|
||||
|
|
|
@ -0,0 +1,201 @@
|
|||
#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 "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;
|
||||
}
|
|
@ -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"));
|
||||
|
|
|
@ -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 */
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue