1 /*
2 * Copyright (c) 1980, 1986, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * %sccs.include.redist.c%
6 */
7
8 #ifndef lint
9 static char sccsid[] = "@(#)utilities.c 8.6 (Berkeley) 05/19/95";
10 #endif /* not lint */
11
12 #include <sys/param.h>
13 #include <sys/time.h>
14
15 #include <ufs/ufs/dinode.h>
16 #include <ufs/ufs/dir.h>
17 #include <ufs/ffs/fs.h>
18
19 #include <ctype.h>
20 #include <err.h>
21 #include <string.h>
22
23 #include "fsck.h"
24
25 long diskreads, totalreads; /* Disk cache statistics */
26
27 static void rwerror __P((char *mesg, ufs_daddr_t blk));
28
29 int
ftypeok(dp)30 ftypeok(dp)
31 struct dinode *dp;
32 {
33 switch (dp->di_mode & IFMT) {
34
35 case IFDIR:
36 case IFREG:
37 case IFBLK:
38 case IFCHR:
39 case IFLNK:
40 case IFSOCK:
41 case IFIFO:
42 return (1);
43
44 default:
45 if (debug)
46 printf("bad file type 0%o\n", dp->di_mode);
47 return (0);
48 }
49 }
50
51 int
reply(question)52 reply(question)
53 char *question;
54 {
55 int persevere;
56 char c;
57
58 if (preen)
59 pfatal("INTERNAL ERROR: GOT TO reply()");
60 persevere = !strcmp(question, "CONTINUE");
61 printf("\n");
62 if (!persevere && (nflag || fswritefd < 0)) {
63 printf("%s? no\n\n", question);
64 return (0);
65 }
66 if (yflag || (persevere && nflag)) {
67 printf("%s? yes\n\n", question);
68 return (1);
69 }
70 do {
71 printf("%s? [yn] ", question);
72 (void) fflush(stdout);
73 c = getc(stdin);
74 while (c != '\n' && getc(stdin) != '\n')
75 if (feof(stdin))
76 return (0);
77 } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N');
78 printf("\n");
79 if (c == 'y' || c == 'Y')
80 return (1);
81 return (0);
82 }
83
84 /*
85 * Malloc buffers and set up cache.
86 */
87 void
bufinit()88 bufinit()
89 {
90 register struct bufarea *bp;
91 long bufcnt, i;
92 char *bufp;
93
94 pbp = pdirbp = (struct bufarea *)0;
95 bufp = malloc((unsigned int)sblock.fs_bsize);
96 if (bufp == 0)
97 errx(EEXIT, "cannot allocate buffer pool");
98 cgblk.b_un.b_buf = bufp;
99 initbarea(&cgblk);
100 bufhead.b_next = bufhead.b_prev = &bufhead;
101 bufcnt = MAXBUFSPACE / sblock.fs_bsize;
102 if (bufcnt < MINBUFS)
103 bufcnt = MINBUFS;
104 for (i = 0; i < bufcnt; i++) {
105 bp = (struct bufarea *)malloc(sizeof(struct bufarea));
106 bufp = malloc((unsigned int)sblock.fs_bsize);
107 if (bp == NULL || bufp == NULL) {
108 if (i >= MINBUFS)
109 break;
110 errx(EEXIT, "cannot allocate buffer pool");
111 }
112 bp->b_un.b_buf = bufp;
113 bp->b_prev = &bufhead;
114 bp->b_next = bufhead.b_next;
115 bufhead.b_next->b_prev = bp;
116 bufhead.b_next = bp;
117 initbarea(bp);
118 }
119 bufhead.b_size = i; /* save number of buffers */
120 }
121
122 /*
123 * Manage a cache of directory blocks.
124 */
125 struct bufarea *
getdatablk(blkno,size)126 getdatablk(blkno, size)
127 ufs_daddr_t blkno;
128 long size;
129 {
130 register struct bufarea *bp;
131
132 for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next)
133 if (bp->b_bno == fsbtodb(&sblock, blkno))
134 goto foundit;
135 for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev)
136 if ((bp->b_flags & B_INUSE) == 0)
137 break;
138 if (bp == &bufhead)
139 errx(EEXIT, "deadlocked buffer pool");
140 getblk(bp, blkno, size);
141 /* fall through */
142 foundit:
143 totalreads++;
144 bp->b_prev->b_next = bp->b_next;
145 bp->b_next->b_prev = bp->b_prev;
146 bp->b_prev = &bufhead;
147 bp->b_next = bufhead.b_next;
148 bufhead.b_next->b_prev = bp;
149 bufhead.b_next = bp;
150 bp->b_flags |= B_INUSE;
151 return (bp);
152 }
153
154 void
getblk(bp,blk,size)155 getblk(bp, blk, size)
156 register struct bufarea *bp;
157 ufs_daddr_t blk;
158 long size;
159 {
160 ufs_daddr_t dblk;
161
162 dblk = fsbtodb(&sblock, blk);
163 if (bp->b_bno != dblk) {
164 flush(fswritefd, bp);
165 diskreads++;
166 bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size);
167 bp->b_bno = dblk;
168 bp->b_size = size;
169 }
170 }
171
172 void
flush(fd,bp)173 flush(fd, bp)
174 int fd;
175 register struct bufarea *bp;
176 {
177 register int i, j;
178
179 if (!bp->b_dirty)
180 return;
181 if (bp->b_errs != 0)
182 pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n",
183 (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ",
184 bp->b_bno);
185 bp->b_dirty = 0;
186 bp->b_errs = 0;
187 bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
188 if (bp != &sblk)
189 return;
190 for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
191 bwrite(fswritefd, (char *)sblock.fs_csp[j],
192 fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
193 sblock.fs_cssize - i < sblock.fs_bsize ?
194 sblock.fs_cssize - i : sblock.fs_bsize);
195 }
196 }
197
198 static void
rwerror(mesg,blk)199 rwerror(mesg, blk)
200 char *mesg;
201 ufs_daddr_t blk;
202 {
203
204 if (preen == 0)
205 printf("\n");
206 pfatal("CANNOT %s: BLK %ld", mesg, blk);
207 if (reply("CONTINUE") == 0)
208 exit(EEXIT);
209 }
210
211 void
ckfini(markclean)212 ckfini(markclean)
213 int markclean;
214 {
215 register struct bufarea *bp, *nbp;
216 int ofsmodified, cnt = 0;
217
218 if (fswritefd < 0) {
219 (void)close(fsreadfd);
220 return;
221 }
222 flush(fswritefd, &sblk);
223 if (havesb && sblk.b_bno != SBOFF / dev_bsize &&
224 !preen && reply("UPDATE STANDARD SUPERBLOCK")) {
225 sblk.b_bno = SBOFF / dev_bsize;
226 sbdirty();
227 flush(fswritefd, &sblk);
228 }
229 flush(fswritefd, &cgblk);
230 free(cgblk.b_un.b_buf);
231 for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) {
232 cnt++;
233 flush(fswritefd, bp);
234 nbp = bp->b_prev;
235 free(bp->b_un.b_buf);
236 free((char *)bp);
237 }
238 if (bufhead.b_size != cnt)
239 errx(EEXIT, "Panic: lost %d buffers", bufhead.b_size - cnt);
240 pbp = pdirbp = (struct bufarea *)0;
241 if (markclean && sblock.fs_clean == 0) {
242 sblock.fs_clean = 1;
243 sbdirty();
244 ofsmodified = fsmodified;
245 flush(fswritefd, &sblk);
246 fsmodified = ofsmodified;
247 if (!preen)
248 printf("\n***** FILE SYSTEM MARKED CLEAN *****\n");
249 }
250 if (debug)
251 printf("cache missed %ld of %ld (%d%%)\n", diskreads,
252 totalreads, (int)(diskreads * 100 / totalreads));
253 (void)close(fsreadfd);
254 (void)close(fswritefd);
255 }
256
257 int
bread(fd,buf,blk,size)258 bread(fd, buf, blk, size)
259 int fd;
260 char *buf;
261 ufs_daddr_t blk;
262 long size;
263 {
264 char *cp;
265 int i, errs;
266 off_t offset;
267
268 offset = blk;
269 offset *= dev_bsize;
270 if (lseek(fd, offset, 0) < 0)
271 rwerror("SEEK", blk);
272 else if (read(fd, buf, (int)size) == size)
273 return (0);
274 rwerror("READ", blk);
275 if (lseek(fd, offset, 0) < 0)
276 rwerror("SEEK", blk);
277 errs = 0;
278 memset(buf, 0, (size_t)size);
279 printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
280 for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
281 if (read(fd, cp, (int)secsize) != secsize) {
282 (void)lseek(fd, offset + i + secsize, 0);
283 if (secsize != dev_bsize && dev_bsize != 1)
284 printf(" %ld (%ld),",
285 (blk * dev_bsize + i) / secsize,
286 blk + i / dev_bsize);
287 else
288 printf(" %ld,", blk + i / dev_bsize);
289 errs++;
290 }
291 }
292 printf("\n");
293 return (errs);
294 }
295
296 void
bwrite(fd,buf,blk,size)297 bwrite(fd, buf, blk, size)
298 int fd;
299 char *buf;
300 ufs_daddr_t blk;
301 long size;
302 {
303 int i;
304 char *cp;
305 off_t offset;
306
307 if (fd < 0)
308 return;
309 offset = blk;
310 offset *= dev_bsize;
311 if (lseek(fd, offset, 0) < 0)
312 rwerror("SEEK", blk);
313 else if (write(fd, buf, (int)size) == size) {
314 fsmodified = 1;
315 return;
316 }
317 rwerror("WRITE", blk);
318 if (lseek(fd, offset, 0) < 0)
319 rwerror("SEEK", blk);
320 printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
321 for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize)
322 if (write(fd, cp, (int)dev_bsize) != dev_bsize) {
323 (void)lseek(fd, offset + i + dev_bsize, 0);
324 printf(" %ld,", blk + i / dev_bsize);
325 }
326 printf("\n");
327 return;
328 }
329
330 /*
331 * allocate a data block with the specified number of fragments
332 */
333 ufs_daddr_t
allocblk(frags)334 allocblk(frags)
335 long frags;
336 {
337 register int i, j, k;
338
339 if (frags <= 0 || frags > sblock.fs_frag)
340 return (0);
341 for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) {
342 for (j = 0; j <= sblock.fs_frag - frags; j++) {
343 if (testbmap(i + j))
344 continue;
345 for (k = 1; k < frags; k++)
346 if (testbmap(i + j + k))
347 break;
348 if (k < frags) {
349 j += k;
350 continue;
351 }
352 for (k = 0; k < frags; k++)
353 setbmap(i + j + k);
354 n_blks += frags;
355 return (i + j);
356 }
357 }
358 return (0);
359 }
360
361 /*
362 * Free a previously allocated block
363 */
364 void
freeblk(blkno,frags)365 freeblk(blkno, frags)
366 ufs_daddr_t blkno;
367 long frags;
368 {
369 struct inodesc idesc;
370
371 idesc.id_blkno = blkno;
372 idesc.id_numfrags = frags;
373 (void)pass4check(&idesc);
374 }
375
376 /*
377 * Find a pathname
378 */
379 void
getpathname(namebuf,curdir,ino)380 getpathname(namebuf, curdir, ino)
381 char *namebuf;
382 ino_t curdir, ino;
383 {
384 int len;
385 register char *cp;
386 struct inodesc idesc;
387 static int busy = 0;
388 extern int findname();
389
390 if (curdir == ino && ino == ROOTINO) {
391 (void)strcpy(namebuf, "/");
392 return;
393 }
394 if (busy ||
395 (statemap[curdir] != DSTATE && statemap[curdir] != DFOUND)) {
396 (void)strcpy(namebuf, "?");
397 return;
398 }
399 busy = 1;
400 memset(&idesc, 0, sizeof(struct inodesc));
401 idesc.id_type = DATA;
402 idesc.id_fix = IGNORE;
403 cp = &namebuf[MAXPATHLEN - 1];
404 *cp = '\0';
405 if (curdir != ino) {
406 idesc.id_parent = curdir;
407 goto namelookup;
408 }
409 while (ino != ROOTINO) {
410 idesc.id_number = ino;
411 idesc.id_func = findino;
412 idesc.id_name = "..";
413 if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
414 break;
415 namelookup:
416 idesc.id_number = idesc.id_parent;
417 idesc.id_parent = ino;
418 idesc.id_func = findname;
419 idesc.id_name = namebuf;
420 if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0)
421 break;
422 len = strlen(namebuf);
423 cp -= len;
424 memmove(cp, namebuf, (size_t)len);
425 *--cp = '/';
426 if (cp < &namebuf[MAXNAMLEN])
427 break;
428 ino = idesc.id_number;
429 }
430 busy = 0;
431 if (ino != ROOTINO)
432 *--cp = '?';
433 memmove(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp));
434 }
435
436 void
catch(sig)437 catch(sig)
438 int sig;
439 {
440 if (!doinglevel2)
441 ckfini(0);
442 exit(12);
443 }
444
445 /*
446 * When preening, allow a single quit to signal
447 * a special exit after filesystem checks complete
448 * so that reboot sequence may be interrupted.
449 */
450 void
catchquit(sig)451 catchquit(sig)
452 int sig;
453 {
454 extern returntosingle;
455
456 printf("returning to single-user after filesystem check\n");
457 returntosingle = 1;
458 (void)signal(SIGQUIT, SIG_DFL);
459 }
460
461 /*
462 * Ignore a single quit signal; wait and flush just in case.
463 * Used by child processes in preen.
464 */
465 void
voidquit(sig)466 voidquit(sig)
467 int sig;
468 {
469
470 sleep(1);
471 (void)signal(SIGQUIT, SIG_IGN);
472 (void)signal(SIGQUIT, SIG_DFL);
473 }
474
475 /*
476 * determine whether an inode should be fixed.
477 */
478 int
dofix(idesc,msg)479 dofix(idesc, msg)
480 register struct inodesc *idesc;
481 char *msg;
482 {
483
484 switch (idesc->id_fix) {
485
486 case DONTKNOW:
487 if (idesc->id_type == DATA)
488 direrror(idesc->id_number, msg);
489 else
490 pwarn(msg);
491 if (preen) {
492 printf(" (SALVAGED)\n");
493 idesc->id_fix = FIX;
494 return (ALTERED);
495 }
496 if (reply("SALVAGE") == 0) {
497 idesc->id_fix = NOFIX;
498 return (0);
499 }
500 idesc->id_fix = FIX;
501 return (ALTERED);
502
503 case FIX:
504 return (ALTERED);
505
506 case NOFIX:
507 case IGNORE:
508 return (0);
509
510 default:
511 errx(EEXIT, "UNKNOWN INODESC FIX MODE %d", idesc->id_fix);
512 }
513 /* NOTREACHED */
514 return (0);
515 }
516
517 #if __STDC__
518 #include <stdarg.h>
519 #else
520 #include <varargs.h>
521 #endif
522
523 /*
524 * An unexpected inconsistency occured.
525 * Die if preening, otherwise just print message and continue.
526 */
527 void
528 #if __STDC__
pfatal(const char * fmt,...)529 pfatal(const char *fmt, ...)
530 #else
531 pfatal(fmt, va_alist)
532 char *fmt;
533 va_dcl
534 #endif
535 {
536 va_list ap;
537 #if __STDC__
538 va_start(ap, fmt);
539 #else
540 va_start(ap);
541 #endif
542 if (!preen) {
543 (void)vfprintf(stderr, fmt, ap);
544 va_end(ap);
545 return;
546 }
547 (void)fprintf(stderr, "%s: ", cdevname);
548 (void)vfprintf(stderr, fmt, ap);
549 (void)fprintf(stderr,
550 "\n%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
551 cdevname);
552 exit(EEXIT);
553 }
554
555 /*
556 * Pwarn just prints a message when not preening,
557 * or a warning (preceded by filename) when preening.
558 */
559 void
560 #if __STDC__
pwarn(const char * fmt,...)561 pwarn(const char *fmt, ...)
562 #else
563 pwarn(fmt, va_alist)
564 char *fmt;
565 va_dcl
566 #endif
567 {
568 va_list ap;
569 #if __STDC__
570 va_start(ap, fmt);
571 #else
572 va_start(ap);
573 #endif
574 if (preen)
575 (void)fprintf(stderr, "%s: ", cdevname);
576 (void)vfprintf(stderr, fmt, ap);
577 va_end(ap);
578 }
579
580 /*
581 * Stub for routines from kernel.
582 */
583 void
584 #if __STDC__
panic(const char * fmt,...)585 panic(const char *fmt, ...)
586 #else
587 panic(fmt, va_alist)
588 char *fmt;
589 va_dcl
590 #endif
591 {
592 va_list ap;
593 #if __STDC__
594 va_start(ap, fmt);
595 #else
596 va_start(ap);
597 #endif
598 pfatal("INTERNAL INCONSISTENCY:");
599 (void)vfprintf(stderr, fmt, ap);
600 va_end(ap);
601 exit(EEXIT);
602 }
603