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