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