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