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