]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/findmnt-verify.c
libblkid: make example more robust
[thirdparty/util-linux.git] / misc-utils / findmnt-verify.c
CommitLineData
c768892f
KZ
1#include <stdio.h>
2#include <stdlib.h>
3#include <errno.h>
4#include <unistd.h>
5#include <stdarg.h>
6#include <string.h>
7#include <sys/types.h>
8#include <sys/stat.h>
c768892f 9#include <libmount.h>
169b4a8c 10#include <blkid.h>
5f1608e0 11#include <sys/utsname.h>
c768892f
KZ
12
13#include "nls.h"
14#include "c.h"
15#include "strutils.h"
16#include "xalloc.h"
161d3fd6 17#include "pathnames.h"
01b63ee3 18#include "match.h"
c768892f
KZ
19
20#include "findmnt.h"
21
22struct verify_context {
23 struct libmnt_fs *fs;
24 struct libmnt_table *tb;
25
5f1608e0
KZ
26 char **fs_ary;
27 size_t fs_num;
28 size_t fs_alloc;
29
c768892f
KZ
30 int nwarnings;
31 int nerrors;
32
f5b7bf15
KZ
33 unsigned int target_printed : 1,
34 no_fsck : 1;
c768892f
KZ
35};
36
ea75960d
KZ
37static void __attribute__ ((__format__ (__printf__, 3, 0)))
38 verify_mesg(struct verify_context *vfy, char type, const char *fmt, va_list ap)
c768892f 39{
161d3fd6 40 if (!vfy->target_printed && vfy->fs) {
c768892f 41 fprintf(stdout, "%s\n", mnt_fs_get_target(vfy->fs));
169b4a8c
KZ
42 vfy->target_printed = 1;
43 }
c768892f
KZ
44
45 fprintf(stdout, " [%c] ", type);
46 vfprintf(stdout, fmt, ap);
47 fputc('\n', stdout);
48}
49
ea75960d
KZ
50static int __attribute__ ((__format__ (__printf__, 2, 3)))
51 verify_warn(struct verify_context *vfy, const char *fmt, ...)
c768892f
KZ
52{
53 va_list ap;
54 vfy->nwarnings++;
55 va_start(ap, fmt);
56 verify_mesg(vfy, 'W', fmt, ap);
57 va_end(ap);
58 return 0;
59}
60
ea75960d
KZ
61static int __attribute__ ((__format__ (__printf__, 2, 3)))
62 verify_err(struct verify_context *vfy, const char *fmt, ...)
c768892f
KZ
63{
64 va_list ap;
65 vfy->nerrors++;
66 va_start(ap, fmt);
67 verify_mesg(vfy, 'E', fmt, ap);
68 va_end(ap);
69 return 0;
70}
71
ea75960d
KZ
72static int __attribute__ ((__format__ (__printf__, 2, 3)))
73 verify_ok(struct verify_context *vfy __attribute__((unused)),
c768892f
KZ
74 const char *fmt, ...)
75{
76 va_list ap;
77
78 if (!(flags & FL_VERBOSE))
79 return 0;
80
81 va_start(ap, fmt);
169b4a8c 82 verify_mesg(vfy, ' ', fmt, ap);
c768892f
KZ
83 va_end(ap);
84 return 0;
85}
86
87static int verify_order(struct verify_context *vfy)
88{
89 struct libmnt_iter *itr = NULL;
90 struct libmnt_fs *next;
91 const char *tgt;
92
93 tgt = mnt_fs_get_target(vfy->fs);
94 if (tgt && !(flags & FL_NOCACHE))
95 tgt = mnt_resolve_target(tgt, cache);
96 else if (!tgt)
97 return 0;
98
99 itr = mnt_new_iter(MNT_ITER_FORWARD);
100 if (!itr) {
101 warn(_("failed to initialize libmount iterator"));
102 goto done;
103 }
104
105 /* set iterator position to 'fs' */
106 mnt_table_set_iter(vfy->tb, itr, vfy->fs);
097c067f
KZ
107
108 if (mnt_table_next_fs(vfy->tb, itr, &next) != 0)
109 goto done;
c768892f
KZ
110
111 /* scan all next filesystems */
112 while (mnt_table_next_fs(vfy->tb, itr, &next) == 0) {
113 const char *n_tgt;
114 size_t len;
115
116 n_tgt = mnt_fs_get_target(next);
117 if (n_tgt && !(flags & FL_NOCACHE))
118 n_tgt = mnt_resolve_target(n_tgt, cache);
119 else if (!n_tgt)
120 continue;
121 len = strlen(n_tgt);
122
123 if (strncmp(n_tgt, tgt, len) == 0) {
124 if (*(tgt + len) == '\0')
125 verify_warn(vfy, _("target specified more than once"));
126 else if (*(tgt + len) == '/')
127 verify_err(vfy, _("wrong order: %s specified before %s"), tgt, n_tgt);
128 }
129 }
130done:
131 mnt_free_iter(itr);
132 return 0;
133}
134
135static int verify_target(struct verify_context *vfy)
136{
137 const char *tgt = mnt_fs_get_target(vfy->fs);
c768892f
KZ
138 struct stat sb;
139
140 if (!tgt)
169b4a8c 141 return verify_err(vfy, _("undefined target (fs_file)"));
c768892f
KZ
142
143 if (!(flags & FL_NOCACHE)) {
c3ae7854 144 const char *cn = mnt_resolve_target(tgt, cache);
c768892f
KZ
145 if (!cn)
146 return -ENOMEM;
147 if (strcmp(cn, tgt) != 0)
148 verify_warn(vfy, _("non-canonical target path (real: %s)"), cn);
149 tgt = cn;
150 }
151 if (stat(tgt, &sb) != 0) {
152 if (mnt_fs_get_option(vfy->fs, "noauto", NULL, NULL) == 1)
169b4a8c 153 verify_err(vfy, _("unreachable on boot required target: %m"));
c768892f 154 else
169b4a8c 155 verify_warn(vfy, _("unreachable target: %m"));
c768892f
KZ
156
157 } else if (!S_ISDIR(sb.st_mode)
158 && mnt_fs_get_option(vfy->fs, "bind", NULL, NULL) == 1) {
159 verify_err(vfy, _("target is not a directory"));
160 } else
169b4a8c 161 verify_ok(vfy, _("target exists"));
c768892f
KZ
162
163 return 0;
164}
165
169b4a8c
KZ
166static char *verify_tag(struct verify_context *vfy, const char *name,
167 const char *value)
168{
3c3f7722
KZ
169 char *src = NULL;
170
171 if (!(flags & FL_NOCACHE))
172 src = mnt_resolve_tag(name, value, cache);
169b4a8c
KZ
173
174 if (!src) {
175 if (mnt_fs_get_option(vfy->fs, "noauto", NULL, NULL) == 1)
176 verify_err(vfy, _("unreachable on boot required source: %s=%s"), name, value);
177 else
178 verify_warn(vfy, _("unreachable: %s=%s"), name, value);
179 } else
180 verify_ok(vfy, _("%s=%s translated to %s"), name, value, src);
181
182 return src;
183}
184
185/* Note that mount source is very FS specific and we should not
186 * interpret unreachable source as error. The exception is only
187 * NAME=value, this has to be convertible to device name.
188 */
189static int verify_source(struct verify_context *vfy)
190{
191 const char *src = mnt_fs_get_srcpath(vfy->fs);
192 char *t = NULL, *v = NULL;
193 struct stat sb;
12fbe36f 194 int isbind, rc = 0;
169b4a8c
KZ
195
196 /* source is NAME=value tag */
197 if (!src) {
198 const char *tag = NULL, *val = NULL;
199
200 if (mnt_fs_get_tag(vfy->fs, &tag, &val) != 0)
201 return verify_err(vfy, _("undefined source (fs_spec)"));
202
203 src = verify_tag(vfy, tag, val);
204 if (!src)
205 goto done;
206
207 /* blkid is able to parse it, but libmount does not see it as a tag --
208 * it means unsupported tag */
12fbe36f
KZ
209 } else if (blkid_parse_tag_string(src, &t, &v) == 0 && stat(src, &sb) != 0) {
210 rc = verify_err(vfy, _("unsupported source tag: %s"), src);
211 goto done;
212 }
169b4a8c
KZ
213 isbind = mnt_fs_get_option(vfy->fs, "bind", NULL, NULL) == 0;
214
215 /* source is path */
216 if (mnt_fs_is_pseudofs(vfy->fs) || mnt_fs_is_netfs(vfy->fs))
a766fafe 217 verify_ok(vfy, _("do not check %s source (pseudo/net)"), src);
169b4a8c
KZ
218
219 else if (stat(src, &sb) != 0)
220 verify_warn(vfy, _("unreachable source: %s: %m"), src);
221
222 else if ((S_ISDIR(sb.st_mode) || S_ISREG(sb.st_mode)) && !isbind)
223 verify_warn(vfy, _("non-bind mount source %s is a directory or regular file"), src);
224
225 else if (!S_ISBLK(sb.st_mode) && !isbind)
226 verify_warn(vfy, _("source %s is not a block device"), src);
227 else
228 verify_ok(vfy, _("source %s exists"), src);
229done:
230 free(t);
231 free(v);
12fbe36f 232 return rc;
169b4a8c
KZ
233}
234
67260dc4
KZ
235static int verify_options(struct verify_context *vfy)
236{
237 const char *opts;
238
239 opts = mnt_fs_get_vfs_options(vfy->fs);
240 if (opts)
241 verify_ok(vfy, _("VFS options: %s"), opts);
242
243 opts = mnt_fs_get_fs_options(vfy->fs);
244 if (opts)
245 verify_ok(vfy, _("FS options: %s"), opts);
246
247 opts = mnt_fs_get_user_options(vfy->fs);
248 if (opts)
249 verify_ok(vfy, _("userspace options: %s"), opts);
250
251 return 0;
252}
253
a1c95432
KZ
254static int verify_swaparea(struct verify_context *vfy)
255{
256 char *arg;
257 size_t argsz = 0;
258
259 if (mnt_fs_get_option(vfy->fs, "discard", &arg, &argsz) == 0
260 && arg
261 && strncmp(arg, "once", argsz) != 0
262 && strncmp(arg, "pages", argsz) != 0)
263 verify_err(vfy, _("unsupported swaparea discard policy: %s"), arg);
264
265 if (mnt_fs_get_option(vfy->fs, "pri", &arg, &argsz) == 0 && arg) {
266 char *p = arg;
267 if (*p == '-')
268 p++;
269 for (; p < arg + argsz; p++) {
270 if (!isdigit((unsigned char) *p)) {
271 verify_err(vfy, _("failed to parse swaparea priority option"));
272 break;
273 }
274 }
275 }
276
277 return 0;
278}
279
5f1608e0
KZ
280static int is_supported_filesystem(struct verify_context *vfy, const char *name)
281{
282 size_t n;
283
284 if (!vfy->fs_num)
285 return 0;
286
287 for (n = 0; n < vfy->fs_num; n++ ) {
01b63ee3 288 if (match_fstype(vfy->fs_ary[n], name))
5f1608e0
KZ
289 return 1;
290 }
291
292 return 0;
293}
294
295static int add_filesystem(struct verify_context *vfy, const char *name)
296{
297 #define MYCHUNK 16
298
299 if (is_supported_filesystem(vfy, name))
300 return 0;
301
302 if (vfy->fs_alloc == 0 || vfy->fs_num + 1 <= vfy->fs_alloc) {
303 vfy->fs_alloc = ((vfy->fs_alloc + 1 + MYCHUNK) / MYCHUNK) * MYCHUNK;
64d6d400 304 vfy->fs_ary = xreallocarray(vfy->fs_ary, vfy->fs_alloc, sizeof(char *));
5f1608e0
KZ
305 }
306
307 vfy->fs_ary[vfy->fs_num] = xstrdup(name);
308 vfy->fs_num++;
309
310 return 0;
311}
312
313static int read_proc_filesystems(struct verify_context *vfy)
314{
315 int rc = 0;
316 FILE *f;
317 char buf[80], *cp, *t;
318
319 f = fopen("/proc/filesystems", "r");
320 if (!f)
321 return -errno;
322
323 while (!feof(f)) {
324 if (!fgets(buf, sizeof(buf), f))
325 break;
326 cp = buf;
327 if (!isspace(*cp)) {
328 while (*cp && !isspace(*cp))
329 cp++;
330 }
331 while (*cp && isspace(*cp))
332 cp++;
333 if ((t = strchr(cp, '\n')) != NULL)
334 *t = 0;
335 if ((t = strchr(cp, '\t')) != NULL)
336 *t = 0;
337 if ((t = strchr(cp, ' ')) != NULL)
338 *t = 0;
339
340 rc = add_filesystem(vfy, cp);
341 if (rc)
79b915e8 342 break;
5f1608e0
KZ
343 }
344 fclose(f);
79b915e8 345 return rc;
5f1608e0
KZ
346}
347
a71f7f11
KZ
348static void free_proc_filesystems(struct verify_context *vfy)
349{
350 size_t n;
351
352 if (!vfy->fs_ary)
353 return;
354
355 for (n = 0; n < vfy->fs_num; n++ )
356 free(vfy->fs_ary[n]);
357 free(vfy->fs_ary);
358}
359
5f1608e0
KZ
360static int read_kernel_filesystems(struct verify_context *vfy)
361{
79b915e8 362 int rc = 0;
5f1608e0
KZ
363#ifdef __linux__
364 struct utsname uts;
365 FILE *f;
366 char buf[1024];
367
368 if (uname(&uts))
369 return 0;
370 snprintf(buf, sizeof(buf), "/lib/modules/%s/modules.dep", uts.release);
371
372 f = fopen(buf, "r");
373 if (!f)
374 return 0;
375
376 while (!feof(f)) {
377 char *p, *name;
5f1608e0
KZ
378
379 if (!fgets(buf, sizeof(buf), f))
380 break;
381
382 if (strncmp("kernel/fs/", buf, 10) != 0 ||
383 strncmp("kernel/fs/nls/", buf, 14) == 0)
384 continue;
385
386 p = strchr(buf, ':');
387 if (!p)
388 continue;
389 *p = '\0';
390
391 name = strrchr(buf, '/');
392 if (!name)
393 continue;
394 name++;
395
396 p = strstr(name, ".ko");
397 if (!p)
398 continue;
399 *p = '\0';
400
401 rc = add_filesystem(vfy, name);
402 if (rc)
79b915e8 403 break;
5f1608e0
KZ
404 }
405 fclose(f);
406#endif /* __linux__ */
79b915e8 407 return rc;
5f1608e0
KZ
408}
409
410static int verify_fstype(struct verify_context *vfy)
411{
3c3f7722
KZ
412 char *src = mnt_resolve_spec(mnt_fs_get_source(vfy->fs), cache);
413 char *realtype = NULL;
414 const char *type;
5f1608e0
KZ
415 int ambi = 0, isauto = 0, isswap = 0;
416
417 if (!src)
418 return 0;
3c3f7722
KZ
419
420 if (mnt_fs_is_pseudofs(vfy->fs) || mnt_fs_is_netfs(vfy->fs)) {
421 verify_ok(vfy, _("do not check %s FS type (pseudo/net)"), src);
422 goto done;
423 }
5f1608e0
KZ
424
425 type = mnt_fs_get_fstype(vfy->fs);
426
427 if (type) {
428 int none = strcmp(type, "none") == 0;
429
430 if (none
431 && mnt_fs_get_option(vfy->fs, "bind", NULL, NULL) == 1
3c3f7722
KZ
432 && mnt_fs_get_option(vfy->fs, "move", NULL, NULL) == 1) {
433 verify_warn(vfy, _("\"none\" FS type is recommended for bind or move oprations only"));
434 goto done;
435 }
5f1608e0 436
042f62df 437 if (strcmp(type, "auto") == 0)
5f1608e0
KZ
438 isauto = 1;
439 else if (strcmp(type, "swap") == 0)
440 isswap = 1;
0b2cd9dc 441 else if (strcmp(type, "xfs") == 0 || strcmp(type, "btrfs") == 0)
f5b7bf15 442 vfy->no_fsck = 1;
5f1608e0
KZ
443
444 if (!isswap && !isauto && !none && !is_supported_filesystem(vfy, type))
31fb9453 445 verify_warn(vfy, _("%s seems unsupported by the current kernel"), type);
5f1608e0 446 }
e209dd35
KZ
447
448 errno = 0;
5f1608e0
KZ
449 realtype = mnt_get_fstype(src, &ambi, cache);
450
451 if (!realtype) {
0b77077c 452 const char *reson = errno ? strerror(errno) : _("reason unknown");
e209dd35 453
5f1608e0 454 if (isauto)
e209dd35 455 verify_err(vfy, _("cannot detect on-disk filesystem type (%s)"), reson);
3c3f7722 456 else
e209dd35 457 verify_warn(vfy, _("cannot detect on-disk filesystem type (%s)"), reson);
3c3f7722 458 goto done;
5f1608e0
KZ
459 }
460
461 if (realtype) {
462 isswap = strcmp(realtype, "swap") == 0;
0b2cd9dc
KZ
463 vfy->no_fsck = strcmp(realtype, "xfs") == 0
464 || strcmp(realtype, "btrfs") == 0;
5f1608e0 465
3c3f7722 466 if (type && !isauto && strcmp(type, realtype) != 0) {
6d9c74af 467 verify_warn(vfy, _("%s does not match with on-disk %s"), type, realtype);
3c3f7722
KZ
468 goto done;
469 }
470 if (!isswap && !is_supported_filesystem(vfy, realtype)) {
6d9c74af 471 verify_warn(vfy, _("on-disk %s seems unsupported by the current kernel"), realtype);
3c3f7722
KZ
472 goto done;
473 }
5f1608e0
KZ
474
475 verify_ok(vfy, _("FS type is %s"), realtype);
476 }
477
3c3f7722
KZ
478done:
479 if (!cache) {
480 free(src);
481 free(realtype);
482 }
5f1608e0
KZ
483 return 0;
484}
485
486static int verify_passno(struct verify_context *vfy)
487{
488 int passno = mnt_fs_get_passno(vfy->fs);
489 const char *tgt = mnt_fs_get_target(vfy->fs);
490
f5b7bf15 491 if (tgt && strcmp("/", tgt) == 0 && passno != 1 && !vfy->no_fsck)
a766fafe 492 return verify_warn(vfy, _("recommended root FS passno is 1 (current is %d)"), passno);
5f1608e0
KZ
493
494 return 0;
495}
496
c768892f
KZ
497static int verify_filesystem(struct verify_context *vfy)
498{
499 int rc = 0;
500
a1c95432
KZ
501 if (mnt_fs_is_swaparea(vfy->fs))
502 rc = verify_swaparea(vfy);
503 else {
c768892f 504 rc = verify_target(vfy);
67260dc4
KZ
505 if (!rc)
506 rc = verify_options(vfy);
507 }
5f1608e0 508
169b4a8c 509 if (!rc)
a1c95432 510 rc = verify_source(vfy);
5f1608e0
KZ
511 if (!rc)
512 rc = verify_fstype(vfy);
513 if (!rc)
f5b7bf15 514 rc = verify_passno(vfy); /* depends on verify_fstype() */
169b4a8c 515
c768892f
KZ
516 return rc;
517}
518
519int verify_table(struct libmnt_table *tb)
520{
521 struct verify_context vfy = { .nerrors = 0 };
1b504263 522 struct libmnt_iter *itr;
c768892f
KZ
523 int rc = 0; /* overall return code (alloc errors, etc.) */
524 int check_order = is_listall_mode();
5f1608e0 525 static int has_read_fs = 0;
c768892f
KZ
526
527 itr = mnt_new_iter(MNT_ITER_FORWARD);
528 if (!itr) {
529 warn(_("failed to initialize libmount iterator"));
530 goto done;
531 }
532
533 vfy.tb = tb;
534
5f1608e0
KZ
535 if (has_read_fs == 0) {
536 read_proc_filesystems(&vfy);
537 read_kernel_filesystems(&vfy);
538 has_read_fs = 1;
539 }
540
c768892f
KZ
541 while (rc == 0 && (vfy.fs = get_next_fs(tb, itr))) {
542 vfy.target_printed = 0;
f5b7bf15
KZ
543 vfy.no_fsck = 0;
544
c768892f
KZ
545 if (check_order)
546 rc = verify_order(&vfy);
547 if (!rc)
548 rc = verify_filesystem(&vfy);
549
550 if (flags & FL_FIRSTONLY)
551 break;
552 flags |= FL_NOSWAPMATCH;
553 }
554
161d3fd6
KZ
555#ifdef USE_SYSTEMD
556 {
557 struct stat a, b;
558
559 if (stat(_PATH_SD_UNITSLOAD, &a) == 0 &&
560 stat(_PATH_MNTTAB, &b) == 0 &&
561 cmp_stat_mtime(&a, &b, <))
562 verify_warn(&vfy, _(
563 "your fstab has been modified, but systemd still uses the old version;\n"
564 " use 'systemctl daemon-reload' to reload"));
565 }
566#endif
567
c768892f
KZ
568done:
569 mnt_free_iter(itr);
570
571 /* summary */
572 if (vfy.nerrors || parse_nerrors || vfy.nwarnings) {
573 fputc('\n', stderr);
574 fprintf(stderr, P_("%d parse error", "%d parse errors", parse_nerrors), parse_nerrors);
575 fprintf(stderr, P_(", %d error", ", %d errors", vfy.nerrors), vfy.nerrors);
576 fprintf(stderr, P_(", %d warning", ", %d warnings", vfy.nwarnings), vfy.nwarnings);
577 fputc('\n', stderr);
578 } else
302419e8 579 fprintf(stdout, _("Success, no errors or warnings detected\n"));
c768892f 580
a71f7f11
KZ
581
582 free_proc_filesystems(&vfy);
583
c768892f
KZ
584 return rc != 0 ? rc : vfy.nerrors + parse_nerrors;
585}