1 /*        $NetBSD: main.c,v 1.92 2023/07/04 20:40:53 riastradh Exp $  */
2 
3 /*
4  * Copyright (c) 1980, 1986, 1993
5  *        The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1980, 1986, 1993\
35  The Regents of the University of California.  All rights reserved.");
36 #endif /* not lint */
37 
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)main.c      8.6 (Berkeley) 5/14/95";
41 #else
42 __RCSID("$NetBSD: main.c,v 1.92 2023/07/04 20:40:53 riastradh Exp $");
43 #endif
44 #endif /* not lint */
45 
46 #include <sys/param.h>
47 #include <sys/time.h>
48 #include <sys/mount.h>
49 #include <sys/resource.h>
50 
51 #include <ufs/ufs/dinode.h>
52 #include <ufs/ufs/ufsmount.h>
53 #include <ufs/ffs/fs.h>
54 #include <ufs/ffs/ffs_extern.h>
55 
56 #include <ctype.h>
57 #include <err.h>
58 #include <errno.h>
59 #include <fstab.h>
60 #include <string.h>
61 #include <time.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <unistd.h>
65 #include <signal.h>
66 
67 #include "fsck.h"
68 #include "extern.h"
69 #include "fsutil.h"
70 #include "exitvalues.h"
71 #include "snapshot.h"
72 
73 struct bufarea bufhead;
74 struct bufarea sblk;
75 struct bufarea asblk;
76 struct bufarea cgblk;
77 struct bufarea appleufsblk;
78 struct bufarea *pdirbp;
79 struct bufarea *pbp;
80 struct fs *sblock;
81 struct fs *altsblock;
82 struct cg *cgrp;
83 struct fs *sblocksave;
84 struct dups *duplist;
85 struct dups *muldup;
86 struct zlncnt *zlnhead;
87 struct inoinfo **inphead, **inpsort;
88 long numdirs, dirhash, listmax, inplast;
89 struct uquot_hash *uquot_user_hash;
90 struct uquot_hash *uquot_group_hash;
91 uint8_t q2h_hash_shift;
92 uint16_t q2h_hash_mask;
93 struct inostatlist *inostathead;
94 long      dev_bsize;
95 long      secsize;
96 char      nflag;
97 char      yflag;
98 int       Uflag;
99 int       bflag;
100 int       debug;
101 int       zflag;
102 int       cvtlevel;
103 int       doinglevel1;
104 int       doinglevel2;
105 int       doing2ea;
106 int       doing2noea;
107 int       newinofmt;
108 char      usedsoftdep;
109 int       preen;
110 int       forceimage;
111 int       is_ufs2;
112 int       is_ufs2ea;
113 int       markclean;
114 char      havesb;
115 char      skipclean;
116 int       fsmodified;
117 int       fsreadfd;
118 int       fswritefd;
119 int       rerun;
120 char      resolved;
121 #ifndef NO_FFS_EI
122 int       endian;
123 int       doswap;
124 int       needswap;
125 int       do_blkswap;
126 int       do_dirswap;
127 #endif
128 #ifndef NO_APPLE_UFS
129 int       isappleufs;
130 #endif
131 daddr_t maxfsblock;
132 char      *blockmap;
133 ino_t     maxino;
134 int       dirblksiz;
135 daddr_t n_blks;
136 ino_t n_files;
137 long countdirs;
138 int       got_siginfo;
139 struct    ufs1_dinode ufs1_zino;
140 struct    ufs2_dinode ufs2_zino;
141 
142 int       progress = 0;
143 
144 static int          argtoi(int, const char *, const char *, int);
145 static int          checkfilesys(const char *, const char *, int);
146 __dead static void  usage(void);
147 
148 int
main(int argc,char * argv[])149 main(int argc, char *argv[])
150 {
151           struct rlimit r;
152           int ch;
153           int ret = FSCK_EXIT_OK;
154           char *snap_backup = NULL;
155           int snap_internal = 0;
156 
157           ckfinish = ckfini;
158 
159           if (getrlimit(RLIMIT_DATA, &r) == 0) {
160                     r.rlim_cur = r.rlim_max;
161                     (void) setrlimit(RLIMIT_DATA, &r);
162           }
163           sync();
164           skipclean = 1;
165           markclean = 1;
166           forceimage = 0;
167 #ifndef NO_FFS_EI
168           endian = 0;
169 #endif
170 #ifndef NO_APPLE_UFS
171           isappleufs = 0;
172 #endif
173           while ((ch = getopt(argc, argv, "aB:b:c:dFfm:npPqUyx:Xz")) != -1) {
174                     switch (ch) {
175 #ifndef NO_APPLE_UFS
176                     case 'a':
177                               isappleufs = 1;
178                               break;
179 #endif
180 
181 #ifndef NO_FFS_EI
182                     case 'B':
183                               if (strcmp(optarg, "be") == 0)
184                                         endian = BIG_ENDIAN;
185                               else if (strcmp(optarg, "le") == 0)
186                                         endian = LITTLE_ENDIAN;
187                               else
188                                         usage();
189                               break;
190 #endif
191 
192                     case 'b':
193                               skipclean = 0;
194                               bflag = argtoi('b', "number", optarg, 10);
195                               printf("Alternate super block location: %d\n", bflag);
196                               break;
197 
198                     case 'c':
199                               skipclean = 0;
200                               if (strcmp(optarg, "ea") == 0) {
201                                         doing2ea = 1;
202                                         break;
203                               }
204                               if (strcmp(optarg, "no-ea") == 0) {
205                                         doing2noea = 1;
206                                         break;
207                               }
208                               cvtlevel = argtoi('c', "conversion level", optarg, 10);
209                               if (cvtlevel > 4) {
210                                         cvtlevel = 4;
211                                         warnx("Using maximum conversion level of %d",
212                                             cvtlevel);
213                               }
214                               break;
215 
216                     case 'd':
217                               debug++;
218                               break;
219 
220                     case 'F':
221                               forceimage = 1;
222                               break;
223 
224                     case 'f':
225                               skipclean = 0;
226                               break;
227 
228                     case 'm':
229                               lfmode = argtoi('m', "mode", optarg, 8);
230                               if (lfmode &~ 07777)
231                                         errx(FSCK_EXIT_USAGE, "bad mode to -m: %o",
232                                             lfmode);
233                               printf("** lost+found creation mode %o\n", lfmode);
234                               break;
235 
236                     case 'n':
237                               nflag++;
238                               yflag = 0;
239                               break;
240 
241                     case 'p':
242                               preen++;
243                               break;
244 
245                     case 'P':
246                               progress = 1;
247                               break;
248 
249                     case 'q':
250                               quiet++;
251                               break;
252 #ifndef SMALL
253                     case 'U':
254                               Uflag++;
255                               break;
256 #endif
257 
258                     case 'x':
259                               snap_backup = optarg;
260                               break;
261 
262                     case 'X':
263                               snap_internal = 1;
264                               break;
265 
266                     case 'y':
267                               yflag++;
268                               nflag = 0;
269                               break;
270 
271                     case 'z':
272                               zflag++;
273                               break;
274 
275                     default:
276                               usage();
277                     }
278           }
279 
280           if (snap_backup || snap_internal) {
281                     if (!nflag || yflag) {
282                               warnx("Cannot use -x or -X without -n");
283                               snap_backup = NULL;
284                               snap_internal = 0;
285                     }
286           }
287 
288           argc -= optind;
289           argv += optind;
290 
291           if (!argc)
292                     usage();
293 
294           if (debug)
295                     progress = 0;
296 
297           if (signal(SIGINT, SIG_IGN) != SIG_IGN)
298                     (void)signal(SIGINT, catch);
299           if (preen)
300                     (void)signal(SIGQUIT, catchquit);
301 #ifdef PROGRESS
302           if (progress) {
303                     progress_ttywidth(0);
304                     (void)signal(SIGWINCH, progress_ttywidth);
305           }
306 #endif /* ! PROGRESS */
307           signal(SIGINFO, infohandler);
308 
309           while (argc-- > 0) {
310                     int nret;
311                     char *path;
312 
313                     if (!forceimage)
314                               path = strdup(blockcheck(*argv));
315                     else
316                               path = strdup(*argv);
317 
318                     if (path == NULL)
319                               pfatal("Can't check %s\n", *argv);
320 
321                     if (snap_backup || snap_internal) {
322                               char *snap_dev;
323                               int snapfd;
324 
325                               snapfd = snap_open(*argv, snap_backup, NULL, &snap_dev);
326                               if (snapfd < 0) {
327                                         warn("can't take snapshot of %s", *argv);
328                                         goto next;
329                               }
330                               nret = checkfilesys(blockcheck(snap_dev), path, 0);
331                               if (ret < nret)
332                                         ret = nret;
333                               close(snapfd);
334                     } else {
335                               nret = checkfilesys(path, path, 0);
336                               if (ret < nret)
337                                         ret = nret;
338                     }
339 next:
340                     free(path);
341                     argv++;
342           }
343 
344           return returntosingle ? FSCK_EXIT_UNRESOLVED : ret;
345 }
346 
347 static int
argtoi(int flag,const char * req,const char * str,int base)348 argtoi(int flag, const char *req, const char *str, int base)
349 {
350           char *cp;
351           int ret;
352 
353           ret = (int)strtol(str, &cp, base);
354           if (cp == str || *cp)
355                     errx(FSCK_EXIT_USAGE, "-%c flag requires a %s",
356                         flag, req);
357           return (ret);
358 }
359 
360 /*
361  * Check the specified filesystem.
362  */
363 /* ARGSUSED */
364 static int
checkfilesys(const char * filesys,const char * origfs,int child)365 checkfilesys(const char *filesys, const char *origfs, int child)
366 {
367           daddr_t n_ffree, n_bfree;
368           struct dups *dp;
369           struct zlncnt *zlnp;
370           uint32_t cylno;
371 #ifdef LITE2BORKEN
372           int flags;
373 #endif
374 #ifdef PROGRESS
375           /*
376            * In prune mode, how far does the progress bar travel during
377            * each pass?  (In non-prune mode, each pass has a separate
378            * progress bar that travels from 0 to 100%.)
379            *
380            * The numbers below are percentages, intended to correspond
381            * roughly to the cumulative time up to the end of each pass.
382            * They don't have to be accurate.  In reality, on a large
383            * file system, Pass 1 and Pass 2 together are likely to use
384            * significantly more than the 95% reflected below, so users
385            * will get a pleasant surprise when the last 5% of the progress
386            * bar runs more quickly than they had expected.
387            */
388           static int progress_limits[] = {0, 20, 95, 96, 97, 100};
389 #endif /* PROGRESS */
390 
391           if (preen && child)
392                     (void)signal(SIGQUIT, voidquit);
393           setcdevname(filesys, preen);
394           if (debug && preen)
395                     pwarn("starting\n");
396           switch (setup(filesys, origfs)) {
397           case 0:
398                     if (preen)
399                               pfatal("CAN'T CHECK FILE SYSTEM.");
400                     /* fall through */
401           case -1:
402                     return FSCK_EXIT_OK;
403           }
404           /*
405            * Cleared if any questions answered no. Used to decide if
406            * the superblock should be marked clean.
407            */
408           resolved = 1;
409 
410 #ifdef PROGRESS
411           progress_switch(progress);
412           progress_init();
413 #endif /* PROGRESS */
414 
415           /*
416            * 1: scan inodes tallying blocks used
417            */
418           if (preen == 0) {
419                     pwarn("** Last Mounted on %s\n", sblock->fs_fsmnt);
420                     if (hotroot())
421                               pwarn("** Root file system\n");
422                     pwarn("** Phase 1 - Check Blocks and Sizes\n");
423           }
424 #ifdef PROGRESS
425           if (preen)
426                     progress_setrange(0, progress_limits[1]);
427 #endif /* PROGRESS */
428           pass1();
429 
430           /*
431            * 1b: locate first references to duplicates, if any
432            */
433           if (duplist) {
434                     if (preen)
435                               pfatal("INTERNAL ERROR: dups with -p\n");
436                     if (usedsoftdep)
437                               pfatal("INTERNAL ERROR: dups with softdep\n");
438                     pwarn("** Phase 1b - Rescan For More DUPS\n");
439                     pass1b();
440           }
441 
442           /*
443            * 2: traverse directories from root to mark all connected directories
444            */
445           if (preen == 0)
446                     pwarn("** Phase 2 - Check Pathnames\n");
447 #ifdef PROGRESS
448           if (preen)
449                     progress_sethighlim(progress_limits[2]);
450 #endif /* PROGRESS */
451           pass2();
452 
453           /*
454            * 3: scan inodes looking for disconnected directories
455            */
456           if (preen == 0)
457                     pwarn("** Phase 3 - Check Connectivity\n");
458 #ifdef PROGRESS
459           if (preen)
460                     progress_sethighlim(progress_limits[3]);
461 #endif /* PROGRESS */
462           pass3();
463 
464           /*
465            * 4: scan inodes looking for disconnected files; check reference counts
466            */
467           if (preen == 0)
468                     pwarn("** Phase 4 - Check Reference Counts\n");
469 #ifdef PROGRESS
470           if (preen)
471                     progress_sethighlim(progress_limits[4]);
472 #endif /* PROGRESS */
473           pass4();
474 
475           /*
476            * 5: check and repair resource counts in cylinder groups
477            */
478           if (preen == 0)
479                     pwarn("** Phase 5 - Check Cyl groups\n");
480 #ifdef PROGRESS
481           if (preen)
482                     progress_sethighlim(progress_limits[5]);
483 #endif /* PROGRESS */
484           pass5();
485           if (uquot_user_hash != NULL) {
486                     if (preen == 0)
487                               pwarn("** Phase 6 - Check Quotas\n");
488                     pass6();
489           }
490 
491           /*
492            * print out summary statistics
493            */
494           n_ffree = sblock->fs_cstotal.cs_nffree;
495           n_bfree = sblock->fs_cstotal.cs_nbfree;
496           pwarn("%llu files, %lld used, %lld free ",
497               (unsigned long long)n_files, (long long)n_blks,
498               (long long)(n_ffree + sblock->fs_frag * n_bfree));
499           printf("(%lld frags, %lld blocks, %lld.%lld%% fragmentation)\n",
500               (long long)n_ffree, (long long)n_bfree,
501               (long long)(n_ffree * 100 / (daddr_t)sblock->fs_dsize),
502               (long long)(((n_ffree * 1000 + (daddr_t)sblock->fs_dsize / 2)
503                     / (daddr_t)sblock->fs_dsize) % 10));
504           if (debug &&
505               (n_files -= maxino - UFS_ROOTINO - sblock->fs_cstotal.cs_nifree))
506                     printf("%llu files missing\n", (unsigned long long)n_files);
507           if (debug) {
508                     n_blks += sblock->fs_ncg *
509                               (cgdmin(sblock, 0) - cgsblock(sblock, 0));
510                     n_blks += cgsblock(sblock, 0) - cgbase(sblock, 0);
511                     n_blks += howmany(sblock->fs_cssize, sblock->fs_fsize);
512                     if (n_blks -= maxfsblock - (n_ffree + sblock->fs_frag * n_bfree))
513                               printf("%lld blocks missing\n", (long long)n_blks);
514                     if (duplist != NULL) {
515                               printf("The following duplicate blocks remain:");
516                               for (dp = duplist; dp; dp = dp->next)
517                                         printf(" %lld,", (long long)dp->dup);
518                               printf("\n");
519                     }
520                     if (zlnhead != NULL) {
521                               printf("The following zero link count inodes remain:");
522                               for (zlnp = zlnhead; zlnp; zlnp = zlnp->next)
523                                         printf(" %llu,",
524                                             (unsigned long long)zlnp->zlncnt);
525                               printf("\n");
526                     }
527           }
528           zlnhead = (struct zlncnt *)0;
529           duplist = (struct dups *)0;
530           muldup = (struct dups *)0;
531           inocleanup();
532           if (fsmodified) {
533                     sblock->fs_time = time(NULL);
534                     sbdirty();
535           }
536           if (rerun)
537                     markclean = 0;
538 #if LITE2BORKEN
539           if (!hotroot()) {
540                     ckfini(1);
541           } else {
542                     struct statvfs stfs_buf;
543                     /*
544                      * Check to see if root is mounted read-write.
545                      */
546                     if (statvfs("/", &stfs_buf) == 0)
547                               flags = stfs_buf.f_flag;
548                     else
549                               flags = 0;
550                     if (markclean)
551                               markclean = flags & MNT_RDONLY;
552                     ckfini(1);
553           }
554 #else
555           ckfini(1);
556 #endif
557           for (cylno = 0; cylno < sblock->fs_ncg; cylno++)
558                     if (inostathead[cylno].il_stat != NULL)
559                               free(inostathead[cylno].il_stat);
560           free(inostathead);
561           inostathead = NULL;
562 
563           if (!resolved || rerun) {
564                     pwarn("\n***** UNRESOLVED INCONSISTENCIES REMAIN *****\n");
565                     returntosingle = 1;
566           }
567           if (!fsmodified)
568                     return FSCK_EXIT_OK;
569           if (!preen)
570                     pwarn("\n***** FILE SYSTEM WAS MODIFIED *****\n");
571           if (rerun)
572                     pwarn("\n***** PLEASE RERUN FSCK *****\n");
573           if (hotroot()) {
574                     struct statvfs stfs_buf;
575                     /*
576                      * We modified the root.  Do a mount update on
577                      * it, unless it is read-write, so we can continue.
578                      */
579                     if (statvfs("/", &stfs_buf) == 0) {
580                               long flags = stfs_buf.f_flag;
581                               struct ufs_args args;
582 
583                               if (flags & MNT_RDONLY) {
584                                         args.fspec = 0;
585                                         flags |= MNT_UPDATE | MNT_RELOAD;
586                                         if (mount(MOUNT_FFS, "/", flags,
587                                             &args, sizeof args) == 0)
588                                                   return FSCK_EXIT_OK;
589                               }
590                     }
591                     if (!preen)
592                               pwarn("\n***** REBOOT NOW *****\n");
593                     sync();
594                     return FSCK_EXIT_ROOT_CHANGED;
595           }
596           return FSCK_EXIT_OK;
597 }
598 
599 static void
usage(void)600 usage(void)
601 {
602 
603           (void) fprintf(stderr,
604               "usage: %s [-"
605 #ifndef NO_APPLE_UFS
606               "a"
607 #endif
608               "dFfPpqUX] "
609 #ifndef NO_FFS_EI
610               "[-B byteorder] "
611 #endif
612               "[-b block] [-c level] [-m mode]\n"
613               "\t[-x snap-backup] [-y | -n] filesystem ...\n",
614               getprogname());
615           exit(FSCK_EXIT_USAGE);
616 }
617