]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sbin/fsck/fsck.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sbin / fsck / fsck.c
1 /*      $NetBSD: fsck.c,v 1.21 1999/04/22 04:20:53 abs Exp $    */
2
3 /*
4  * Copyright (c) 1996 Christos Zoulas. All rights reserved.
5  * Copyright (c) 1980, 1989, 1993, 1994
6  *      The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by the University of
19  *      California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * From: @(#)mount.c    8.19 (Berkeley) 4/19/94
37  * From: $NetBSD: mount.c,v 1.24 1995/11/18 03:34:29 cgd Exp 
38  * $NetBSD: fsck.c,v 1.21 1999/04/22 04:20:53 abs Exp $
39  */
40
41 #include <sys/cdefs.h>
42 __FBSDID("$FreeBSD$");
43
44 #include <sys/param.h>
45 #include <sys/mount.h>
46 #include <sys/queue.h>
47 #include <sys/wait.h>
48 #define FSTYPENAMES
49 #include <sys/disklabel.h>
50 #include <sys/ioctl.h>
51
52 #include <ctype.h>
53 #include <err.h>
54 #include <errno.h>
55 #include <fstab.h>
56 #include <fcntl.h>
57 #include <paths.h>
58 #include <signal.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <unistd.h>
63
64 #include "fsutil.h"
65
66 static enum { IN_LIST, NOT_IN_LIST } which = NOT_IN_LIST;
67
68 TAILQ_HEAD(fstypelist, entry) opthead, selhead;
69
70 struct entry {
71         char *type;
72         char *options;
73         TAILQ_ENTRY(entry) entries;
74 };
75
76 static char *options = NULL;
77 static int flags = 0;
78 static int forceflag = 0;
79
80 static int checkfs(const char *, const char *, const char *, char *, pid_t *);
81 static int selected(const char *);
82 static void addoption(char *);
83 static const char *getoptions(const char *);
84 static void addentry(struct fstypelist *, const char *, const char *);
85 static void maketypelist(char *);
86 static void catopt(char **, const char *);
87 static void mangle(char *, int *, const char ***, int *);
88 static const char *getfslab(const char *);
89 static void usage(void) __dead2;
90 static int isok(struct fstab *);
91
92 int
93 main(int argc, char *argv[])
94 {
95         struct fstab *fs;
96         int i, rval = 0;
97         const char *vfstype = NULL;
98         char globopt[3];
99
100         globopt[0] = '-';
101         globopt[2] = '\0';
102
103         TAILQ_INIT(&selhead);
104         TAILQ_INIT(&opthead);
105
106         while ((i = getopt(argc, argv, "BCdvpfFnyl:t:T:")) != -1)
107                 switch (i) {
108                 case 'B':
109                         if (flags & CHECK_BACKGRD)
110                                 errx(1, "Cannot specify -B and -F.");
111                         flags |= DO_BACKGRD;
112                         break;
113
114                 case 'd':
115                         flags |= CHECK_DEBUG;
116                         break;
117
118                 case 'v':
119                         flags |= CHECK_VERBOSE;
120                         break;
121
122                 case 'F':
123                         if (flags & DO_BACKGRD)
124                                 errx(1, "Cannot specify -B and -F.");
125                         flags |= CHECK_BACKGRD;
126                         break;
127
128                 case 'p':
129                         flags |= CHECK_PREEN;
130                         /*FALLTHROUGH*/
131                 case 'C':
132                         flags |= CHECK_CLEAN;
133                         /*FALLTHROUGH*/
134                 case 'n':
135                 case 'y':
136                         globopt[1] = i;
137                         catopt(&options, globopt);
138                         break;
139
140                 case 'f':
141                         forceflag = 1;
142                         globopt[1] = i;
143                         catopt(&options, globopt);
144                         break;
145
146                 case 'l':
147                         warnx("Ignoring obsolete -l option\n");
148                         break;
149
150                 case 'T':
151                         if (*optarg)
152                                 addoption(optarg);
153                         break;
154
155                 case 't':
156                         if (!TAILQ_EMPTY(&selhead))
157                                 errx(1, "only one -t option may be specified.");
158
159                         maketypelist(optarg);
160                         vfstype = optarg;
161                         break;
162
163                 case '?':
164                 default:
165                         usage();
166                         /* NOTREACHED */
167                 }
168
169         argc -= optind;
170         argv += optind;
171
172         if (argc == 0)
173                 return checkfstab(flags, isok, checkfs);
174
175 #define BADTYPE(type)                                                   \
176         (strcmp(type, FSTAB_RO) &&                                      \
177             strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ))
178
179
180         for (; argc--; argv++) {
181                 const char *spec, *mntpt, *type, *cp;
182                 char device[MAXPATHLEN];
183                 struct statfs *mntp;
184
185                 spec = *argv;
186                 cp = strrchr(spec, '/');
187                 if (cp == 0) {
188                         (void)snprintf(device, sizeof(device), "%s%s",
189                                 _PATH_DEV, spec);
190                         spec = device;
191                 }
192                 mntp = getmntpt(spec);
193                 if (mntp != NULL) {
194                         spec = mntp->f_mntfromname;
195                         mntpt = mntp->f_mntonname;
196                 }
197                 if ((fs = getfsfile(spec)) == NULL &&
198                     (fs = getfsspec(spec)) == NULL) {
199                         if (vfstype == NULL)
200                                 vfstype = getfslab(spec);
201                         if (vfstype == NULL)
202                                 errx(1, "Could not determine filesystem type");
203                         type = vfstype;
204                         devcheck(spec);
205                 } else {
206                         spec = fs->fs_spec;
207                         type = fs->fs_vfstype;
208                         mntpt = fs->fs_file;
209                         if (BADTYPE(fs->fs_type))
210                                 errx(1, "%s has unknown file system type.",
211                                     spec);
212                 }
213                 if ((flags & CHECK_BACKGRD) &&
214                     checkfs(type, spec, mntpt, "-F", NULL) == 0) {
215                         printf("%s: DEFER FOR BACKGROUND CHECKING\n", *argv);
216                         continue;
217                 }
218                 if ((flags & DO_BACKGRD) && forceflag == 0 &&
219                     checkfs(type, spec, mntpt, "-F", NULL) != 0)
220                         continue;
221
222                 rval |= checkfs(type, spec, mntpt, NULL, NULL);
223         }
224
225         return rval;
226 }
227
228
229 static int
230 isok(struct fstab *fs)
231 {
232         int i;
233
234         if (fs->fs_passno == 0)
235                 return (0);
236         if (BADTYPE(fs->fs_type))
237                 return (0);
238         if (!selected(fs->fs_vfstype))
239                 return (0);
240         /*
241          * If the -B flag has been given, then process the needed
242          * background checks. Background checks cannot be run on
243          * file systems that will be mounted read-only or that were
244          * not mounted at boot time (typically those marked `noauto').
245          * If these basic tests are passed, check with the file system
246          * itself to see if it is willing to do background checking
247          * by invoking its check program with the -F flag.
248          */
249         if (flags & DO_BACKGRD) {
250                 if (!strcmp(fs->fs_type, FSTAB_RO))
251                         return (0);
252                 if (getmntpt(fs->fs_spec) == NULL)
253                         return (0);
254                 if (checkfs(fs->fs_vfstype, fs->fs_spec, fs->fs_file, "-F", 0))
255                         return (0);
256                 return (1);
257         }
258         /*
259          * If the -F flag has been given, then consider deferring the
260          * check to background. Background checks cannot be run on
261          * file systems that will be mounted read-only or that will
262          * not be mounted at boot time (e.g., marked `noauto'). If
263          * these basic tests are passed, check with the file system
264          * itself to see if it is willing to defer to background
265          * checking by invoking its check program with the -F flag.
266          */
267         if ((flags & CHECK_BACKGRD) == 0 || !strcmp(fs->fs_type, FSTAB_RO))
268                 return (1);
269         for (i = strlen(fs->fs_mntops) - 6; i >= 0; i--)
270                 if (!strncmp(&fs->fs_mntops[i], "noauto", 6))
271                         break;
272         if (i >= 0)
273                 return (1);
274         if (checkfs(fs->fs_vfstype, fs->fs_spec, fs->fs_file, "-F", NULL) != 0)
275                 return (1);
276         printf("%s: DEFER FOR BACKGROUND CHECKING\n", fs->fs_spec);
277         return (0);
278 }
279
280
281 static int
282 checkfs(const char *pvfstype, const char *spec, const char *mntpt,
283     char *auxopt, pid_t *pidp)
284 {
285         const char **argv;
286         pid_t pid;
287         int argc, i, status, maxargc;
288         char *optbuf, execbase[MAXPATHLEN];
289         char *vfstype = NULL;
290         const char *extra = NULL;
291
292 #ifdef __GNUC__
293         /* Avoid vfork clobbering */
294         (void) &optbuf;
295         (void) &vfstype;
296 #endif
297         /*
298          * We convert the vfstype to lowercase and any spaces to underscores
299          * to not confuse the issue
300          *
301          * XXX This is a kludge to make automatic filesystem type guessing
302          * from the disklabel work for "4.2BSD" filesystems.  It does a
303          * very limited subset of transliteration to a normalised form of
304          * filesystem name, and we do not seem to enforce a filesystem
305          * name character set.
306          */
307         vfstype = strdup(pvfstype);
308         if (vfstype == NULL)
309                 perror("strdup(pvfstype)"); 
310         for (i = 0; i < strlen(vfstype); i++) {
311                 vfstype[i] = tolower(vfstype[i]);
312                 if (vfstype[i] == ' ')
313                         vfstype[i] = '_';
314         }
315
316         extra = getoptions(vfstype);
317         optbuf = NULL;
318         if (options)
319                 catopt(&optbuf, options);
320         if (extra)
321                 catopt(&optbuf, extra);
322         if (auxopt)
323                 catopt(&optbuf, auxopt);
324         else if (flags & DO_BACKGRD)
325                 catopt(&optbuf, "-B");
326
327         maxargc = 64;
328         argv = emalloc(sizeof(char *) * maxargc);
329
330         (void) snprintf(execbase, sizeof(execbase), "fsck_%s", vfstype);
331         argc = 0;
332         argv[argc++] = execbase;
333         if (optbuf)
334                 mangle(optbuf, &argc, &argv, &maxargc);
335         argv[argc++] = spec;
336         argv[argc] = NULL;
337
338         if (flags & (CHECK_DEBUG|CHECK_VERBOSE)) {
339                 (void)printf("start %s %swait", mntpt, 
340                         pidp ? "no" : "");
341                 for (i = 0; i < argc; i++)
342                         (void)printf(" %s", argv[i]);
343                 (void)printf("\n");
344         }
345
346         switch (pid = vfork()) {
347         case -1:                                /* Error. */
348                 warn("vfork");
349                 if (optbuf)
350                         free(optbuf);
351                 free(vfstype);
352                 return (1);
353
354         case 0:                                 /* Child. */
355                 if ((flags & CHECK_DEBUG) && auxopt == NULL)
356                         _exit(0);
357
358                 /* Go find an executable. */
359                 execvP(execbase, _PATH_SYSPATH, (char * const *)argv);
360                 if (spec)
361                         warn("exec %s for %s in %s", execbase, spec, _PATH_SYSPATH);
362                 else
363                         warn("exec %s in %s", execbase, _PATH_SYSPATH);
364                 _exit(1);
365                 /* NOTREACHED */
366
367         default:                                /* Parent. */
368                 if (optbuf)
369                         free(optbuf);
370
371                 free(vfstype);
372
373                 if (pidp) {
374                         *pidp = pid;
375                         return 0;
376                 }
377
378                 if (waitpid(pid, &status, 0) < 0) {
379                         warn("waitpid");
380                         return (1);
381                 }
382
383                 if (WIFEXITED(status)) {
384                         if (WEXITSTATUS(status) != 0)
385                                 return (WEXITSTATUS(status));
386                 }
387                 else if (WIFSIGNALED(status)) {
388                         warnx("%s: %s", spec, strsignal(WTERMSIG(status)));
389                         return (1);
390                 }
391                 break;
392         }
393
394         return (0);
395 }
396
397
398 static int
399 selected(const char *type)
400 {
401         struct entry *e;
402
403         /* If no type specified, it's always selected. */
404         TAILQ_FOREACH(e, &selhead, entries)
405                 if (!strncmp(e->type, type, MFSNAMELEN))
406                         return which == IN_LIST ? 1 : 0;
407
408         return which == IN_LIST ? 0 : 1;
409 }
410
411
412 static const char *
413 getoptions(const char *type)
414 {
415         struct entry *e;
416
417         TAILQ_FOREACH(e, &opthead, entries)
418                 if (!strncmp(e->type, type, MFSNAMELEN))
419                         return e->options;
420         return "";
421 }
422
423
424 static void
425 addoption(char *optstr)
426 {
427         char *newoptions;
428         struct entry *e;
429
430         if ((newoptions = strchr(optstr, ':')) == NULL)
431                 errx(1, "Invalid option string");
432
433         *newoptions++ = '\0';
434
435         TAILQ_FOREACH(e, &opthead, entries)
436                 if (!strncmp(e->type, optstr, MFSNAMELEN)) {
437                         catopt(&e->options, newoptions);
438                         return;
439                 }
440         addentry(&opthead, optstr, newoptions);
441 }
442
443
444 static void
445 addentry(struct fstypelist *list, const char *type, const char *opts)
446 {
447         struct entry *e;
448
449         e = emalloc(sizeof(struct entry));
450         e->type = estrdup(type);
451         e->options = estrdup(opts);
452         TAILQ_INSERT_TAIL(list, e, entries);
453 }
454
455
456 static void
457 maketypelist(char *fslist)
458 {
459         char *ptr;
460
461         if ((fslist == NULL) || (fslist[0] == '\0'))
462                 errx(1, "empty type list");
463
464         if (fslist[0] == 'n' && fslist[1] == 'o') {
465                 fslist += 2;
466                 which = NOT_IN_LIST;
467         }
468         else
469                 which = IN_LIST;
470
471         while ((ptr = strsep(&fslist, ",")) != NULL)
472                 addentry(&selhead, ptr, "");
473
474 }
475
476
477 static void
478 catopt(char **sp, const char *o)
479 {
480         char *s;
481         size_t i, j;
482
483         s = *sp;
484         if (s) {
485                 i = strlen(s);
486                 j = i + 1 + strlen(o) + 1;
487                 s = erealloc(s, j);
488                 (void)snprintf(s + i, j, ",%s", o);
489         } else
490                 s = estrdup(o);
491         *sp = s;
492 }
493
494
495 static void
496 mangle(char *options, int *argcp, const char ***argvp, int *maxargcp)
497 {
498         char *p, *s;
499         int argc, maxargc;
500         const char **argv;
501
502         argc = *argcp;
503         argv = *argvp;
504         maxargc = *maxargcp;
505
506         for (s = options; (p = strsep(&s, ",")) != NULL;) {
507                 /* Always leave space for one more argument and the NULL. */
508                 if (argc >= maxargc - 3) {
509                         maxargc <<= 1;
510                         argv = erealloc(argv, maxargc * sizeof(char *));
511                 }
512                 if (*p != '\0')  {
513                         if (*p == '-') {
514                                 argv[argc++] = p;
515                                 p = strchr(p, '=');
516                                 if (p) {
517                                         *p = '\0';
518                                         argv[argc++] = p+1;
519                                 }
520                         } else {
521                                 argv[argc++] = "-o";
522                                 argv[argc++] = p;
523                         }
524                 }
525         }
526
527         *argcp = argc;
528         *argvp = argv;
529         *maxargcp = maxargc;
530 }
531
532
533 const static char *
534 getfslab(const char *str)
535 {
536         struct disklabel dl;
537         int fd;
538         char p;
539         const char *vfstype;
540         u_char t;
541
542         /* deduce the file system type from the disk label */
543         if ((fd = open(str, O_RDONLY)) == -1)
544                 err(1, "cannot open `%s'", str);
545
546         if (ioctl(fd, DIOCGDINFO, &dl) == -1) {
547                 (void) close(fd);
548                 return(NULL);
549         }
550
551         (void) close(fd);
552
553         p = str[strlen(str) - 1];
554
555         if ((p - 'a') >= dl.d_npartitions)
556                 errx(1, "partition `%s' is not defined on disk", str);
557
558         if ((t = dl.d_partitions[p - 'a'].p_fstype) >= FSMAXTYPES) 
559                 errx(1, "partition `%s' is not of a legal vfstype",
560                     str);
561
562         if ((vfstype = fstypenames[t]) == NULL)
563                 errx(1, "vfstype `%s' on partition `%s' is not supported",
564                     fstypenames[t], str);
565
566         return vfstype;
567 }
568
569
570 static void
571 usage(void)
572 {
573         static const char common[] =
574             "[-Cdfnpvy] [-B | -F] [-T fstype:fsoptions] [-t fstype]";
575
576         (void)fprintf(stderr, "usage: %s %s [special | node] ...\n",
577             getprogname(), common);
578         exit(1);
579 }