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