]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - misc-utils/findmnt-verify.c
11 #include <sys/utsname.h>
18 #include "pathnames.h"
23 struct verify_context
{
25 struct libmnt_table
*tb
;
34 unsigned int target_printed
: 1,
38 static void __attribute__ ((__format__ (__printf__
, 3, 0)))
39 verify_mesg(struct verify_context
*vfy
, char type
, const char *fmt
, va_list ap
)
41 if (!vfy
->target_printed
&& vfy
->fs
) {
42 fprintf(stdout
, "%s\n", mnt_fs_get_target(vfy
->fs
));
43 vfy
->target_printed
= 1;
46 fprintf(stdout
, " [%c] ", type
);
47 vfprintf(stdout
, fmt
, ap
);
51 static int __attribute__ ((__format__ (__printf__
, 2, 3)))
52 verify_warn(struct verify_context
*vfy
, const char *fmt
, ...)
57 verify_mesg(vfy
, 'W', fmt
, ap
);
62 static int __attribute__ ((__format__ (__printf__
, 2, 3)))
63 verify_err(struct verify_context
*vfy
, const char *fmt
, ...)
68 verify_mesg(vfy
, 'E', fmt
, ap
);
73 static int __attribute__ ((__format__ (__printf__
, 3, 4)))
74 verify_ok(struct verify_context
*vfy
__attribute__((unused
)),
84 verify_mesg(vfy
, ' ', fmt
, ap
);
89 static int verify_order(struct verify_context
*vfy
, struct findmnt
*findmnt
)
91 struct libmnt_iter
*itr
= NULL
;
92 struct libmnt_fs
*next
;
95 tgt
= mnt_fs_get_target(vfy
->fs
);
96 if (tgt
&& !(findmnt
->flags
& FL_NOCACHE
))
97 tgt
= mnt_resolve_target(tgt
, findmnt
->cache
);
101 itr
= mnt_new_iter(MNT_ITER_FORWARD
);
103 warn(_("failed to initialize libmount iterator"));
107 /* set iterator position to 'fs' */
108 mnt_table_set_iter(vfy
->tb
, itr
, vfy
->fs
);
110 if (mnt_table_next_fs(vfy
->tb
, itr
, &next
) != 0)
113 /* scan all next filesystems */
114 while (mnt_table_next_fs(vfy
->tb
, itr
, &next
) == 0) {
118 n_tgt
= mnt_fs_get_target(next
);
119 if (n_tgt
&& !(findmnt
->flags
& FL_NOCACHE
))
120 n_tgt
= mnt_resolve_target(n_tgt
, findmnt
->cache
);
125 if (strncmp(n_tgt
, tgt
, len
) == 0) {
126 if (*(tgt
+ len
) == '\0')
127 verify_warn(vfy
, _("target specified more than once"));
128 else if (*(tgt
+ len
) == '/')
129 verify_err(vfy
, _("wrong order: %s specified before %s"), tgt
, n_tgt
);
137 static int verify_target(struct verify_context
*vfy
, struct findmnt
*findmnt
)
139 const char *tgt
= mnt_fs_get_target(vfy
->fs
);
143 return verify_err(vfy
, _("undefined target (fs_file)"));
145 if (!(findmnt
->flags
& FL_NOCACHE
)) {
146 const char *cn
= mnt_resolve_target(tgt
, findmnt
->cache
);
149 if (strcmp(cn
, tgt
) != 0)
150 verify_warn(vfy
, _("non-canonical target path (real: %s)"), cn
);
153 if (stat(tgt
, &sb
) != 0) {
154 if (mnt_fs_get_option(vfy
->fs
, "noauto", NULL
, NULL
) == 1)
155 verify_err(vfy
, _("unreachable on boot required target: %m"));
157 verify_warn(vfy
, _("unreachable target: %m"));
159 } else if (!S_ISDIR(sb
.st_mode
)
160 && mnt_fs_get_option(vfy
->fs
, "bind", NULL
, NULL
) == 1) {
161 verify_err(vfy
, _("target is not a directory"));
163 verify_ok(vfy
, findmnt
->flags
& FL_VERBOSE
, _("target exists"));
168 static char *verify_tag(struct verify_context
*vfy
, const char *name
,
169 const char *value
, struct findmnt
*findmnt
)
173 if (!(findmnt
->flags
& FL_NOCACHE
))
174 src
= mnt_resolve_tag(name
, value
, findmnt
->cache
);
177 if (mnt_fs_get_option(vfy
->fs
, "noauto", NULL
, NULL
) == 1)
178 verify_err(vfy
, _("unreachable on boot required source: %s=%s"), name
, value
);
180 verify_warn(vfy
, _("unreachable: %s=%s"), name
, value
);
182 verify_ok(vfy
, findmnt
->flags
& FL_VERBOSE
, _("%s=%s translated to %s"), name
, value
, src
);
187 /* Note that mount source is very FS specific and we should not
188 * interpret unreachable source as error. The exception is only
189 * NAME=value, this has to be convertible to device name.
191 static int verify_source(struct verify_context
*vfy
, struct findmnt
*findmnt
)
193 const char *src
= mnt_fs_get_srcpath(vfy
->fs
);
194 char *t
= NULL
, *v
= NULL
;
198 /* source is NAME=value tag */
200 const char *tag
= NULL
, *val
= NULL
;
202 if (mnt_fs_get_tag(vfy
->fs
, &tag
, &val
) != 0)
203 return verify_err(vfy
, _("undefined source (fs_spec)"));
205 src
= verify_tag(vfy
, tag
, val
, findmnt
);
209 /* blkid is able to parse it, but libmount does not see it as a tag --
210 * it means unsupported tag */
211 } else if (blkid_parse_tag_string(src
, &t
, &v
) == 0 && stat(src
, &sb
) != 0) {
212 rc
= verify_err(vfy
, _("unsupported source tag: %s"), src
);
215 isbind
= mnt_fs_get_option(vfy
->fs
, "bind", NULL
, NULL
) == 0;
218 if (mnt_fs_is_pseudofs(vfy
->fs
) || mnt_fs_is_netfs(vfy
->fs
))
219 verify_ok(vfy
, findmnt
->flags
& FL_VERBOSE
, _("do not check %s source (pseudo/net)"), src
);
221 else if (stat(src
, &sb
) != 0)
222 verify_warn(vfy
, _("unreachable source: %s: %m"), src
);
224 else if ((S_ISDIR(sb
.st_mode
) || S_ISREG(sb
.st_mode
)) && !isbind
)
225 verify_warn(vfy
, _("non-bind mount source %s is a directory or regular file"), src
);
227 else if (!S_ISBLK(sb
.st_mode
) && !isbind
)
228 verify_warn(vfy
, _("source %s is not a block device"), src
);
230 verify_ok(vfy
, findmnt
->flags
& FL_VERBOSE
, _("source %s exists"), src
);
237 static int verify_options(struct verify_context
*vfy
, bool verbose
)
241 opts
= mnt_fs_get_vfs_options(vfy
->fs
);
243 verify_ok(vfy
, verbose
, _("VFS options: %s"), opts
);
245 opts
= mnt_fs_get_fs_options(vfy
->fs
);
247 verify_ok(vfy
, verbose
, _("FS options: %s"), opts
);
249 opts
= mnt_fs_get_user_options(vfy
->fs
);
251 verify_ok(vfy
, verbose
, _("userspace options: %s"), opts
);
256 static int verify_swaparea(struct verify_context
*vfy
)
261 if (mnt_fs_get_option(vfy
->fs
, "discard", &arg
, &argsz
) == 0
263 && strncmp(arg
, "once", argsz
) != 0
264 && strncmp(arg
, "pages", argsz
) != 0)
265 verify_err(vfy
, _("unsupported swaparea discard policy: %s"), arg
);
267 if (mnt_fs_get_option(vfy
->fs
, "pri", &arg
, &argsz
) == 0 && arg
) {
271 for (; p
< arg
+ argsz
; p
++) {
272 if (!isdigit((unsigned char) *p
)) {
273 verify_err(vfy
, _("failed to parse swaparea priority option"));
282 static int is_supported_filesystem(struct verify_context
*vfy
, const char *name
)
289 for (n
= 0; n
< vfy
->fs_num
; n
++ ) {
290 if (match_fstype(vfy
->fs_ary
[n
], name
))
297 static int add_filesystem(struct verify_context
*vfy
, const char *name
)
301 if (is_supported_filesystem(vfy
, name
))
304 if (vfy
->fs_alloc
== 0 || vfy
->fs_num
+ 1 <= vfy
->fs_alloc
) {
305 vfy
->fs_alloc
= ((vfy
->fs_alloc
+ 1 + MYCHUNK
) / MYCHUNK
) * MYCHUNK
;
306 vfy
->fs_ary
= xreallocarray(vfy
->fs_ary
, vfy
->fs_alloc
, sizeof(char *));
309 vfy
->fs_ary
[vfy
->fs_num
] = xstrdup(name
);
315 static int read_proc_filesystems(struct verify_context
*vfy
)
319 char buf
[80], *cp
, *t
;
321 f
= fopen("/proc/filesystems", "r");
326 if (!fgets(buf
, sizeof(buf
), f
))
330 while (*cp
&& !isspace(*cp
))
333 while (*cp
&& isspace(*cp
))
335 if ((t
= strchr(cp
, '\n')) != NULL
)
337 if ((t
= strchr(cp
, '\t')) != NULL
)
339 if ((t
= strchr(cp
, ' ')) != NULL
)
342 rc
= add_filesystem(vfy
, cp
);
350 static void free_proc_filesystems(struct verify_context
*vfy
)
357 for (n
= 0; n
< vfy
->fs_num
; n
++ )
358 free(vfy
->fs_ary
[n
]);
362 static int read_kernel_filesystems(struct verify_context
*vfy
)
372 snprintf(buf
, sizeof(buf
), "/lib/modules/%s/modules.dep", uts
.release
);
381 if (!fgets(buf
, sizeof(buf
), f
))
384 if (strncmp("kernel/fs/", buf
, 10) != 0 ||
385 strncmp("kernel/fs/nls/", buf
, 14) == 0)
388 p
= strchr(buf
, ':');
393 name
= strrchr(buf
, '/');
398 p
= strstr(name
, ".ko");
403 rc
= add_filesystem(vfy
, name
);
408 #endif /* __linux__ */
412 static int verify_fstype(struct verify_context
*vfy
, struct findmnt
*findmnt
)
414 char *src
= mnt_resolve_spec(mnt_fs_get_source(vfy
->fs
), findmnt
->cache
);
415 char *realtype
= NULL
;
417 int ambi
= 0, isauto
= 0, isswap
= 0;
422 if (mnt_fs_is_pseudofs(vfy
->fs
) || mnt_fs_is_netfs(vfy
->fs
)) {
423 verify_ok(vfy
, findmnt
->flags
& FL_VERBOSE
, _("do not check %s FS type (pseudo/net)"), src
);
427 type
= mnt_fs_get_fstype(vfy
->fs
);
430 int none
= strcmp(type
, "none") == 0;
433 && mnt_fs_get_option(vfy
->fs
, "bind", NULL
, NULL
) == 1
434 && mnt_fs_get_option(vfy
->fs
, "move", NULL
, NULL
) == 1) {
435 verify_warn(vfy
, _("\"none\" FS type is recommended for bind or move operations only"));
439 if (strcmp(type
, "auto") == 0)
441 else if (strcmp(type
, "swap") == 0)
443 else if (strcmp(type
, "xfs") == 0 || strcmp(type
, "btrfs") == 0)
446 if (!isswap
&& !isauto
&& !none
&& !is_supported_filesystem(vfy
, type
))
447 verify_warn(vfy
, _("%s seems unsupported by the current kernel"), type
);
451 realtype
= mnt_get_fstype(src
, &ambi
, findmnt
->cache
);
454 const char *reason
= errno
? strerror(errno
) : _("reason unknown");
457 verify_err(vfy
, _("cannot detect on-disk filesystem type (%s)"), reason
);
459 verify_warn(vfy
, _("cannot detect on-disk filesystem type (%s)"), reason
);
464 isswap
= strcmp(realtype
, "swap") == 0;
465 vfy
->no_fsck
= strcmp(realtype
, "xfs") == 0
466 || strcmp(realtype
, "btrfs") == 0;
468 if (type
&& !isauto
&& strcmp(type
, realtype
) != 0) {
469 verify_warn(vfy
, _("%s does not match with on-disk %s"), type
, realtype
);
472 if (!isswap
&& !is_supported_filesystem(vfy
, realtype
)) {
473 verify_warn(vfy
, _("on-disk %s seems unsupported by the current kernel"), realtype
);
477 verify_ok(vfy
, findmnt
->flags
& FL_VERBOSE
, _("FS type is %s"), realtype
);
481 if (!findmnt
->cache
) {
488 static int verify_passno(struct verify_context
*vfy
)
490 int passno
= mnt_fs_get_passno(vfy
->fs
);
491 const char *tgt
= mnt_fs_get_target(vfy
->fs
);
493 if (tgt
&& strcmp("/", tgt
) == 0 && passno
!= 1 && !vfy
->no_fsck
)
494 return verify_warn(vfy
, _("recommended root FS passno is 1 (current is %d)"), passno
);
499 static int verify_filesystem(struct verify_context
*vfy
, struct findmnt
*findmnt
)
503 if (mnt_fs_is_swaparea(vfy
->fs
))
504 rc
= verify_swaparea(vfy
);
506 rc
= verify_target(vfy
, findmnt
);
508 rc
= verify_options(vfy
, findmnt
->flags
& FL_VERBOSE
);
512 rc
= verify_source(vfy
, findmnt
);
514 rc
= verify_fstype(vfy
, findmnt
);
516 rc
= verify_passno(vfy
); /* depends on verify_fstype() */
521 int verify_table(struct libmnt_table
*tb
, struct findmnt
*findmnt
)
523 struct verify_context vfy
= { .nerrors
= 0 };
524 struct libmnt_iter
*itr
;
525 int rc
= 0; /* overall return code (alloc errors, etc.) */
526 int check_order
= is_listall_mode(findmnt
->flags
);
527 static int has_read_fs
= 0;
529 itr
= mnt_new_iter(MNT_ITER_FORWARD
);
531 warn(_("failed to initialize libmount iterator"));
537 if (has_read_fs
== 0) {
538 read_proc_filesystems(&vfy
);
539 read_kernel_filesystems(&vfy
);
543 while (rc
== 0 && (vfy
.fs
= get_next_fs(tb
, itr
, findmnt
))) {
544 vfy
.target_printed
= 0;
548 rc
= verify_order(&vfy
, findmnt
);
550 rc
= verify_filesystem(&vfy
, findmnt
);
552 if (findmnt
->flags
& FL_FIRSTONLY
)
554 findmnt
->flags
|= FL_NOSWAPMATCH
;
561 if (stat(_PATH_SD_UNITSLOAD
, &a
) == 0 &&
562 stat(_PATH_MNTTAB
, &b
) == 0 &&
563 cmp_stat_mtime(&a
, &b
, <))
565 "your fstab has been modified, but systemd still uses the old version;\n"
566 " use 'systemctl daemon-reload' to reload"));
574 if (vfy
.nerrors
|| findmnt
->parse_nerrors
|| vfy
.nwarnings
) {
576 fprintf(stderr
, P_("%d parse error", "%d parse errors", findmnt
->parse_nerrors
), findmnt
->parse_nerrors
);
577 fprintf(stderr
, P_(", %d error", ", %d errors", vfy
.nerrors
), vfy
.nerrors
);
578 fprintf(stderr
, P_(", %d warning", ", %d warnings", vfy
.nwarnings
), vfy
.nwarnings
);
581 fprintf(stdout
, _("Success, no errors or warnings detected\n"));
584 free_proc_filesystems(&vfy
);
586 return rc
!= 0 ? rc
: vfy
.nerrors
+ findmnt
->parse_nerrors
;