utilities.c revision 1.10 1 /*
2 * Copyright (c) 1980, 1986, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #ifndef lint
35 /*static char sccsid[] = "from: @(#)utilities.c 8.1 (Berkeley) 6/5/93";*/
36 static char *rcsid = "$Id: utilities.c,v 1.10 1994/09/23 14:27:22 mycroft Exp $";
37 #endif /* not lint */
38
39 #include <sys/param.h>
40 #include <sys/time.h>
41 #include <ufs/ufs/dinode.h>
42 #include <ufs/ufs/dir.h>
43 #include <ufs/ffs/fs.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <ctype.h>
48 #include "fsck.h"
49
50 long diskreads, totalreads; /* Disk cache statistics */
51
52 ftypeok(dp)
53 struct dinode *dp;
54 {
55 switch (dp->di_mode & IFMT) {
56
57 case IFDIR:
58 case IFREG:
59 case IFBLK:
60 case IFCHR:
61 case IFLNK:
62 case IFSOCK:
63 case IFIFO:
64 return (1);
65
66 default:
67 if (debug)
68 printf("bad file type 0%o\n", dp->di_mode);
69 return (0);
70 }
71 }
72
73 reply(question)
74 char *question;
75 {
76 int persevere;
77 char c;
78
79 if (preen)
80 pfatal("INTERNAL ERROR: GOT TO reply()");
81 persevere = !strcmp(question, "CONTINUE");
82 printf("\n");
83 if (!persevere && (nflag || fswritefd < 0)) {
84 printf("%s? no\n\n", question);
85 return (0);
86 }
87 if (yflag || (persevere && nflag)) {
88 printf("%s? yes\n\n", question);
89 return (1);
90 }
91 do {
92 printf("%s? [yn] ", question);
93 (void) fflush(stdout);
94 c = getc(stdin);
95 while (c != '\n' && getc(stdin) != '\n')
96 if (feof(stdin))
97 return (0);
98 } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N');
99 printf("\n");
100 if (c == 'y' || c == 'Y')
101 return (1);
102 return (0);
103 }
104
105 /*
106 * Malloc buffers and set up cache.
107 */
108 bufinit()
109 {
110 register struct bufarea *bp;
111 long bufcnt, i;
112 char *bufp;
113
114 pbp = pdirbp = (struct bufarea *)0;
115 bufp = malloc((unsigned int)sblock.fs_bsize);
116 if (bufp == 0)
117 errexit("cannot allocate buffer pool\n");
118 cgblk.b_un.b_buf = bufp;
119 initbarea(&cgblk);
120 bufhead.b_next = bufhead.b_prev = &bufhead;
121 bufcnt = MAXBUFSPACE / sblock.fs_bsize;
122 if (bufcnt < MINBUFS)
123 bufcnt = MINBUFS;
124 for (i = 0; i < bufcnt; i++) {
125 bp = (struct bufarea *)malloc(sizeof(struct bufarea));
126 bufp = malloc((unsigned int)sblock.fs_bsize);
127 if (bp == NULL || bufp == NULL) {
128 if (i >= MINBUFS)
129 break;
130 errexit("cannot allocate buffer pool\n");
131 }
132 bp->b_un.b_buf = bufp;
133 bp->b_prev = &bufhead;
134 bp->b_next = bufhead.b_next;
135 bufhead.b_next->b_prev = bp;
136 bufhead.b_next = bp;
137 initbarea(bp);
138 }
139 bufhead.b_size = i; /* save number of buffers */
140 }
141
142 /*
143 * Manage a cache of directory blocks.
144 */
145 struct bufarea *
146 getdatablk(blkno, size)
147 daddr_t blkno;
148 long size;
149 {
150 register struct bufarea *bp;
151
152 for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next)
153 if (bp->b_bno == fsbtodb(&sblock, blkno))
154 goto foundit;
155 for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev)
156 if ((bp->b_flags & B_INUSE) == 0)
157 break;
158 if (bp == &bufhead)
159 errexit("deadlocked buffer pool\n");
160 getblk(bp, blkno, size);
161 /* fall through */
162 foundit:
163 totalreads++;
164 bp->b_prev->b_next = bp->b_next;
165 bp->b_next->b_prev = bp->b_prev;
166 bp->b_prev = &bufhead;
167 bp->b_next = bufhead.b_next;
168 bufhead.b_next->b_prev = bp;
169 bufhead.b_next = bp;
170 bp->b_flags |= B_INUSE;
171 return (bp);
172 }
173
174 void
175 getblk(bp, blk, size)
176 register struct bufarea *bp;
177 daddr_t blk;
178 long size;
179 {
180 daddr_t dblk;
181
182 dblk = fsbtodb(&sblock, blk);
183 if (bp->b_bno != dblk) {
184 flush(fswritefd, bp);
185 diskreads++;
186 bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size);
187 bp->b_bno = dblk;
188 bp->b_size = size;
189 }
190 }
191
192 flush(fd, bp)
193 int fd;
194 register struct bufarea *bp;
195 {
196 register int i, j;
197
198 if (!bp->b_dirty)
199 return;
200 if (bp->b_errs != 0)
201 pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n",
202 (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ",
203 bp->b_bno);
204 bp->b_dirty = 0;
205 bp->b_errs = 0;
206 bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
207 if (bp != &sblk)
208 return;
209 for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
210 bwrite(fswritefd, (char *)sblock.fs_csp[j],
211 fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
212 sblock.fs_cssize - i < sblock.fs_bsize ?
213 sblock.fs_cssize - i : sblock.fs_bsize);
214 }
215 }
216
217 rwerror(mesg, blk)
218 char *mesg;
219 daddr_t blk;
220 {
221
222 if (preen == 0)
223 printf("\n");
224 pfatal("CANNOT %s: BLK %ld", mesg, blk);
225 if (reply("CONTINUE") == 0)
226 errexit("Program terminated\n");
227 }
228
229 ckfini()
230 {
231 register struct bufarea *bp, *nbp;
232 int cnt = 0;
233
234 if (fswritefd < 0) {
235 (void)close(fsreadfd);
236 return;
237 }
238 flush(fswritefd, &sblk);
239 if (havesb && sblk.b_bno != SBOFF / dev_bsize &&
240 !preen && reply("UPDATE STANDARD SUPERBLOCK")) {
241 sblk.b_bno = SBOFF / dev_bsize;
242 sbdirty();
243 flush(fswritefd, &sblk);
244 }
245 flush(fswritefd, &cgblk);
246 free(cgblk.b_un.b_buf);
247 for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) {
248 cnt++;
249 flush(fswritefd, bp);
250 nbp = bp->b_prev;
251 free(bp->b_un.b_buf);
252 free((char *)bp);
253 }
254 if (bufhead.b_size != cnt)
255 errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt);
256 pbp = pdirbp = (struct bufarea *)0;
257 if (debug)
258 printf("cache missed %ld of %ld (%d%%)\n", diskreads,
259 totalreads, (int)(diskreads * 100 / totalreads));
260 (void)close(fsreadfd);
261 (void)close(fswritefd);
262 }
263
264 bread(fd, buf, blk, size)
265 int fd;
266 char *buf;
267 daddr_t blk;
268 long size;
269 {
270 char *cp;
271 int i, errs;
272 off_t offset;
273
274 offset = blk;
275 offset *= dev_bsize;
276 if (lseek(fd, offset, 0) < 0)
277 rwerror("SEEK", blk);
278 else if (read(fd, buf, (int)size) == size)
279 return (0);
280 rwerror("READ", blk);
281 if (lseek(fd, offset, 0) < 0)
282 rwerror("SEEK", blk);
283 errs = 0;
284 memset(buf, 0, (size_t)size);
285 printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
286 for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
287 if (read(fd, cp, (int)secsize) != secsize) {
288 (void)lseek(fd, offset + i + secsize, 0);
289 if (secsize != dev_bsize && dev_bsize != 1)
290 printf(" %ld (%ld),",
291 (blk * dev_bsize + i) / secsize,
292 blk + i / dev_bsize);
293 else
294 printf(" %ld,", blk + i / dev_bsize);
295 errs++;
296 }
297 }
298 printf("\n");
299 return (errs);
300 }
301
302 bwrite(fd, buf, blk, size)
303 int fd;
304 char *buf;
305 daddr_t blk;
306 long size;
307 {
308 int i;
309 char *cp;
310 off_t offset;
311
312 if (fd < 0)
313 return;
314 offset = blk;
315 offset *= dev_bsize;
316 if (lseek(fd, offset, 0) < 0)
317 rwerror("SEEK", blk);
318 else if (write(fd, buf, (int)size) == size) {
319 fsmodified = 1;
320 return;
321 }
322 rwerror("WRITE", blk);
323 if (lseek(fd, offset, 0) < 0)
324 rwerror("SEEK", blk);
325 printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
326 for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize)
327 if (write(fd, cp, (int)dev_bsize) != dev_bsize) {
328 (void)lseek(fd, offset + i + dev_bsize, 0);
329 printf(" %ld,", blk + i / dev_bsize);
330 }
331 printf("\n");
332 return;
333 }
334
335 /*
336 * allocate a data block with the specified number of fragments
337 */
338 allocblk(frags)
339 long frags;
340 {
341 register int i, j, k;
342
343 if (frags <= 0 || frags > sblock.fs_frag)
344 return (0);
345 for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) {
346 for (j = 0; j <= sblock.fs_frag - frags; j++) {
347 if (testbmap(i + j))
348 continue;
349 for (k = 1; k < frags; k++)
350 if (testbmap(i + j + k))
351 break;
352 if (k < frags) {
353 j += k;
354 continue;
355 }
356 for (k = 0; k < frags; k++)
357 setbmap(i + j + k);
358 n_blks += frags;
359 return (i + j);
360 }
361 }
362 return (0);
363 }
364
365 /*
366 * Free a previously allocated block
367 */
368 freeblk(blkno, frags)
369 daddr_t blkno;
370 long frags;
371 {
372 struct inodesc idesc;
373
374 idesc.id_blkno = blkno;
375 idesc.id_numfrags = frags;
376 (void)pass4check(&idesc);
377 }
378
379 /*
380 * Find a pathname
381 */
382 getpathname(namebuf, curdir, ino)
383 char *namebuf;
384 ino_t curdir, ino;
385 {
386 int len;
387 register char *cp;
388 struct inodesc idesc;
389 static int busy = 0;
390 extern int findname();
391
392 if (curdir == ino && ino == ROOTINO) {
393 (void)strcpy(namebuf, "/");
394 return;
395 }
396 if (busy ||
397 (statemap[curdir] != DSTATE && statemap[curdir] != DFOUND)) {
398 (void)strcpy(namebuf, "?");
399 return;
400 }
401 busy = 1;
402 memset(&idesc, 0, sizeof(struct inodesc));
403 idesc.id_type = DATA;
404 idesc.id_fix = IGNORE;
405 cp = &namebuf[MAXPATHLEN - 1];
406 *cp = '\0';
407 if (curdir != ino) {
408 idesc.id_parent = curdir;
409 goto namelookup;
410 }
411 while (ino != ROOTINO) {
412 idesc.id_number = ino;
413 idesc.id_func = findino;
414 idesc.id_name = "..";
415 if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
416 break;
417 namelookup:
418 idesc.id_number = idesc.id_parent;
419 idesc.id_parent = ino;
420 idesc.id_func = findname;
421 idesc.id_name = namebuf;
422 if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0)
423 break;
424 len = strlen(namebuf);
425 cp -= len;
426 memcpy(cp, namebuf, (size_t)len);
427 *--cp = '/';
428 if (cp < &namebuf[MAXNAMLEN])
429 break;
430 ino = idesc.id_number;
431 }
432 busy = 0;
433 if (ino != ROOTINO)
434 *--cp = '?';
435 memcpy(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp));
436 }
437
438 void
439 catch()
440 {
441 if (!doinglevel2)
442 ckfini();
443 exit(12);
444 }
445
446 /*
447 * When preening, allow a single quit to signal
448 * a special exit after filesystem checks complete
449 * so that reboot sequence may be interrupted.
450 */
451 void
452 catchquit()
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
466 voidquit()
467 {
468
469 sleep(1);
470 (void)signal(SIGQUIT, SIG_IGN);
471 (void)signal(SIGQUIT, SIG_DFL);
472 }
473
474 /*
475 * determine whether an inode should be fixed.
476 */
477 dofix(idesc, msg)
478 register struct inodesc *idesc;
479 char *msg;
480 {
481
482 switch (idesc->id_fix) {
483
484 case DONTKNOW:
485 if (idesc->id_type == DATA)
486 direrror(idesc->id_number, msg);
487 else
488 pwarn(msg);
489 if (preen) {
490 printf(" (SALVAGED)\n");
491 idesc->id_fix = FIX;
492 return (ALTERED);
493 }
494 if (reply("SALVAGE") == 0) {
495 idesc->id_fix = NOFIX;
496 return (0);
497 }
498 idesc->id_fix = FIX;
499 return (ALTERED);
500
501 case FIX:
502 return (ALTERED);
503
504 case NOFIX:
505 case IGNORE:
506 return (0);
507
508 default:
509 errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix);
510 }
511 /* NOTREACHED */
512 }
513
514 /* VARARGS1 */
515 errexit(s1, s2, s3, s4)
516 char *s1;
517 {
518 printf(s1, s2, s3, s4);
519 exit(8);
520 }
521
522 /*
523 * An unexpected inconsistency occured.
524 * Die if preening, otherwise just print message and continue.
525 */
526 /* VARARGS1 */
527 pfatal(s, a1, a2, a3)
528 char *s;
529 {
530
531 if (preen) {
532 printf("%s: ", cdevname);
533 printf(s, a1, a2, a3);
534 printf("\n");
535 printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
536 cdevname);
537 exit(8);
538 }
539 printf(s, a1, a2, a3);
540 }
541
542 /*
543 * Pwarn just prints a message when not preening,
544 * or a warning (preceded by filename) when preening.
545 */
546 /* VARARGS1 */
547 pwarn(s, a1, a2, a3, a4, a5, a6)
548 char *s;
549 {
550
551 if (preen)
552 printf("%s: ", cdevname);
553 printf(s, a1, a2, a3, a4, a5, a6);
554 }
555
556 #ifndef lint
557 /*
558 * Stub for routines from kernel.
559 */
560 panic(s)
561 char *s;
562 {
563
564 pfatal("INTERNAL INCONSISTENCY:");
565 errexit(s);
566 }
567 #endif
568