utilities.c revision 1.7 1 /*
2 * Copyright (c) 1980, 1986 The Regents of the University of California.
3 * 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 5.30 (Berkeley) 7/26/91";*/
36 static char rcsid[] = "$Id: utilities.c,v 1.7 1994/04/25 18:29:08 cgd Exp $";
37 #endif /* not lint */
38
39 #include <sys/param.h>
40 #include <sys/time.h>
41 #include <ufs/dinode.h>
42 #include <ufs/fs.h>
43 #include <ufs/dir.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 flush(fswritefd, &sblk);
235 if (havesb && sblk.b_bno != SBOFF / dev_bsize &&
236 !preen && reply("UPDATE STANDARD SUPERBLOCK")) {
237 sblk.b_bno = SBOFF / dev_bsize;
238 sbdirty();
239 flush(fswritefd, &sblk);
240 }
241 flush(fswritefd, &cgblk);
242 free(cgblk.b_un.b_buf);
243 for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) {
244 cnt++;
245 flush(fswritefd, bp);
246 nbp = bp->b_prev;
247 free(bp->b_un.b_buf);
248 free((char *)bp);
249 }
250 if (bufhead.b_size != cnt)
251 errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt);
252 pbp = pdirbp = (struct bufarea *)0;
253 if (debug)
254 printf("cache missed %ld of %ld (%d%%)\n", diskreads,
255 totalreads, (int)(diskreads * 100 / totalreads));
256 (void)close(fsreadfd);
257 (void)close(fswritefd);
258 }
259
260 bread(fd, buf, blk, size)
261 int fd;
262 char *buf;
263 daddr_t blk;
264 long size;
265 {
266 char *cp;
267 int i, errs;
268
269 if (lseek(fd, blk * dev_bsize, 0) < 0)
270 rwerror("SEEK", blk);
271 else if (read(fd, buf, (int)size) == size)
272 return (0);
273 rwerror("READ", blk);
274 if (lseek(fd, blk * dev_bsize, 0) < 0)
275 rwerror("SEEK", blk);
276 errs = 0;
277 bzero(buf, (size_t)size);
278 printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
279 for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
280 if (read(fd, cp, (int)secsize) != secsize) {
281 (void)lseek(fd, blk * dev_bsize + i + secsize, 0);
282 if (secsize != dev_bsize && dev_bsize != 1)
283 printf(" %ld (%ld),",
284 (blk * dev_bsize + i) / secsize,
285 blk + i / dev_bsize);
286 else
287 printf(" %ld,", blk + i / dev_bsize);
288 errs++;
289 }
290 }
291 printf("\n");
292 return (errs);
293 }
294
295 bwrite(fd, buf, blk, size)
296 int fd;
297 char *buf;
298 daddr_t blk;
299 long size;
300 {
301 int i;
302 char *cp;
303
304 if (fd < 0)
305 return;
306 if (lseek(fd, blk * dev_bsize, 0) < 0)
307 rwerror("SEEK", blk);
308 else if (write(fd, buf, (int)size) == size) {
309 fsmodified = 1;
310 return;
311 }
312 rwerror("WRITE", blk);
313 if (lseek(fd, blk * dev_bsize, 0) < 0)
314 rwerror("SEEK", blk);
315 printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
316 for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize)
317 if (write(fd, cp, (int)dev_bsize) != dev_bsize) {
318 (void)lseek(fd, blk * dev_bsize + i + dev_bsize, 0);
319 printf(" %ld,", blk + i / dev_bsize);
320 }
321 printf("\n");
322 return;
323 }
324
325 /*
326 * allocate a data block with the specified number of fragments
327 */
328 allocblk(frags)
329 long frags;
330 {
331 register int i, j, k;
332
333 if (frags <= 0 || frags > sblock.fs_frag)
334 return (0);
335 for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) {
336 for (j = 0; j <= sblock.fs_frag - frags; j++) {
337 if (testbmap(i + j))
338 continue;
339 for (k = 1; k < frags; k++)
340 if (testbmap(i + j + k))
341 break;
342 if (k < frags) {
343 j += k;
344 continue;
345 }
346 for (k = 0; k < frags; k++)
347 setbmap(i + j + k);
348 n_blks += frags;
349 return (i + j);
350 }
351 }
352 return (0);
353 }
354
355 /*
356 * Free a previously allocated block
357 */
358 freeblk(blkno, frags)
359 daddr_t blkno;
360 long frags;
361 {
362 struct inodesc idesc;
363
364 idesc.id_blkno = blkno;
365 idesc.id_numfrags = frags;
366 (void)pass4check(&idesc);
367 }
368
369 /*
370 * Find a pathname
371 */
372 getpathname(namebuf, curdir, ino)
373 char *namebuf;
374 ino_t curdir, ino;
375 {
376 int len;
377 register char *cp;
378 struct inodesc idesc;
379 static int busy = 0;
380 extern int findname();
381
382 if (busy ||
383 (statemap[curdir] != DSTATE && statemap[curdir] != DFOUND)) {
384 (void)strcpy(namebuf, "?");
385 return;
386 }
387 busy = 1;
388 bzero((char *)&idesc, sizeof(struct inodesc));
389 idesc.id_type = DATA;
390 idesc.id_fix = IGNORE;
391 cp = &namebuf[MAXPATHLEN - 1];
392 *cp = '\0';
393 if (curdir != ino) {
394 idesc.id_parent = curdir;
395 goto namelookup;
396 }
397 while (ino != ROOTINO) {
398 idesc.id_number = ino;
399 idesc.id_func = findino;
400 idesc.id_name = "..";
401 if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
402 break;
403 namelookup:
404 idesc.id_number = idesc.id_parent;
405 idesc.id_parent = ino;
406 idesc.id_func = findname;
407 idesc.id_name = namebuf;
408 if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0)
409 break;
410 len = strlen(namebuf);
411 cp -= len;
412 bcopy(namebuf, cp, (size_t)len);
413 *--cp = '/';
414 if (cp < &namebuf[MAXNAMLEN])
415 break;
416 ino = idesc.id_number;
417 }
418 busy = 0;
419 if (ino != ROOTINO)
420 *--cp = '?';
421 bcopy(cp, namebuf, (size_t)(&namebuf[MAXPATHLEN] - cp));
422 }
423
424 void
425 catch()
426 {
427 ckfini();
428 exit(12);
429 }
430
431 /*
432 * When preening, allow a single quit to signal
433 * a special exit after filesystem checks complete
434 * so that reboot sequence may be interrupted.
435 */
436 void
437 catchquit()
438 {
439 extern returntosingle;
440
441 printf("returning to single-user after filesystem check\n");
442 returntosingle = 1;
443 (void)signal(SIGQUIT, SIG_DFL);
444 }
445
446 /*
447 * Ignore a single quit signal; wait and flush just in case.
448 * Used by child processes in preen.
449 */
450 void
451 voidquit()
452 {
453
454 sleep(1);
455 (void)signal(SIGQUIT, SIG_IGN);
456 (void)signal(SIGQUIT, SIG_DFL);
457 }
458
459 /*
460 * determine whether an inode should be fixed.
461 */
462 dofix(idesc, msg)
463 register struct inodesc *idesc;
464 char *msg;
465 {
466
467 switch (idesc->id_fix) {
468
469 case DONTKNOW:
470 if (idesc->id_type == DATA)
471 direrror(idesc->id_number, msg);
472 else
473 pwarn(msg);
474 if (preen) {
475 printf(" (SALVAGED)\n");
476 idesc->id_fix = FIX;
477 return (ALTERED);
478 }
479 if (reply("SALVAGE") == 0) {
480 idesc->id_fix = NOFIX;
481 return (0);
482 }
483 idesc->id_fix = FIX;
484 return (ALTERED);
485
486 case FIX:
487 return (ALTERED);
488
489 case NOFIX:
490 case IGNORE:
491 return (0);
492
493 default:
494 errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix);
495 }
496 /* NOTREACHED */
497 }
498
499 /* VARARGS1 */
500 errexit(s1, s2, s3, s4)
501 char *s1;
502 {
503 printf(s1, s2, s3, s4);
504 exit(8);
505 }
506
507 /*
508 * An unexpected inconsistency occured.
509 * Die if preening, otherwise just print message and continue.
510 */
511 /* VARARGS1 */
512 pfatal(s, a1, a2, a3)
513 char *s;
514 {
515
516 if (preen) {
517 printf("%s: ", devname);
518 printf(s, a1, a2, a3);
519 printf("\n");
520 printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
521 devname);
522 exit(8);
523 }
524 printf(s, a1, a2, a3);
525 }
526
527 /*
528 * Pwarn just prints a message when not preening,
529 * or a warning (preceded by filename) when preening.
530 */
531 /* VARARGS1 */
532 pwarn(s, a1, a2, a3, a4, a5, a6)
533 char *s;
534 {
535
536 if (preen)
537 printf("%s: ", devname);
538 printf(s, a1, a2, a3, a4, a5, a6);
539 }
540
541 #ifndef lint
542 /*
543 * Stub for routines from kernel.
544 */
545 panic(s)
546 char *s;
547 {
548
549 pfatal("INTERNAL INCONSISTENCY:");
550 errexit(s);
551 }
552 #endif
553