update from Rick Macklem, with further semantic refinements by me
[unix-history] / usr / src / sbin / fsck / utilities.c
CommitLineData
76797561 1/*
fe32782c
KM
2 * Copyright (c) 1980, 1986 The Regents of the University of California.
3 * All rights reserved.
4 *
70ab3c27 5 * %sccs.include.redist.c%
76797561
DF
6 */
7
5d9906c0 8#ifndef lint
558b3a30 9static char sccsid[] = "@(#)utilities.c 5.31 (Berkeley) %G%";
fe32782c 10#endif /* not lint */
5d9906c0 11
5d9906c0 12#include <sys/param.h>
558b3a30
KB
13#include <ufs/ufs/dinode.h>
14#include <ufs/ufs/dir.h>
15#include <ufs/ffs/fs.h>
392fe950 16#include <stdio.h>
d72e970b
KM
17#include <stdlib.h>
18#include <string.h>
392fe950 19#include <ctype.h>
5d9906c0
KM
20#include "fsck.h"
21
adc5a10c 22long diskreads, totalreads; /* Disk cache statistics */
5d9906c0
KM
23long lseek();
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
569ec282 207 flush(fswritefd, &sblk);
55f8bbd7 208 if (havesb && sblk.b_bno != SBOFF / dev_bsize &&
6eb81197 209 !preen && reply("UPDATE STANDARD SUPERBLOCK")) {
bc5c7748 210 sblk.b_bno = SBOFF / dev_bsize;
5d9906c0 211 sbdirty();
569ec282 212 flush(fswritefd, &sblk);
5d9906c0 213 }
569ec282 214 flush(fswritefd, &cgblk);
3249f52d
KM
215 free(cgblk.b_un.b_buf);
216 for (bp = bufhead.b_prev; bp != &bufhead; bp = nbp) {
d8f697fc 217 cnt++;
569ec282 218 flush(fswritefd, bp);
3249f52d
KM
219 nbp = bp->b_prev;
220 free(bp->b_un.b_buf);
221 free((char *)bp);
d8f697fc
KM
222 }
223 if (bufhead.b_size != cnt)
224 errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt);
c0dee198 225 pbp = pdirbp = (struct bufarea *)0;
adc5a10c 226 if (debug)
d72e970b
KM
227 printf("cache missed %ld of %ld (%d%%)\n", diskreads,
228 totalreads, (int)(diskreads * 100 / totalreads));
569ec282
KM
229 (void)close(fsreadfd);
230 (void)close(fswritefd);
5d9906c0
KM
231}
232
569ec282
KM
233bread(fd, buf, blk, size)
234 int fd;
5d9906c0
KM
235 char *buf;
236 daddr_t blk;
237 long size;
238{
49505034
KM
239 char *cp;
240 int i, errs;
241
569ec282
KM
242 if (lseek(fd, blk * dev_bsize, 0) < 0)
243 rwerror("SEEK", blk);
244 else if (read(fd, buf, (int)size) == size)
49505034 245 return (0);
569ec282
KM
246 rwerror("READ", blk);
247 if (lseek(fd, blk * dev_bsize, 0) < 0)
248 rwerror("SEEK", blk);
49505034 249 errs = 0;
d72e970b 250 bzero(buf, (size_t)size);
919c85da
MK
251 printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
252 for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
57195990 253 if (read(fd, cp, (int)secsize) != secsize) {
d72e970b 254 (void)lseek(fd, blk * dev_bsize + i + secsize, 0);
55f8bbd7 255 if (secsize != dev_bsize && dev_bsize != 1)
d72e970b 256 printf(" %ld (%ld),",
919c85da
MK
257 (blk * dev_bsize + i) / secsize,
258 blk + i / dev_bsize);
259 else
d72e970b 260 printf(" %ld,", blk + i / dev_bsize);
49505034
KM
261 errs++;
262 }
263 }
cee9cbac 264 printf("\n");
49505034 265 return (errs);
5d9906c0
KM
266}
267
569ec282
KM
268bwrite(fd, buf, blk, size)
269 int fd;
5d9906c0
KM
270 char *buf;
271 daddr_t blk;
272 long size;
273{
cee9cbac
KM
274 int i;
275 char *cp;
5d9906c0 276
569ec282 277 if (fd < 0)
cee9cbac 278 return;
569ec282
KM
279 if (lseek(fd, blk * dev_bsize, 0) < 0)
280 rwerror("SEEK", blk);
281 else if (write(fd, buf, (int)size) == size) {
282 fsmodified = 1;
cee9cbac 283 return;
5d9906c0 284 }
569ec282
KM
285 rwerror("WRITE", blk);
286 if (lseek(fd, blk * dev_bsize, 0) < 0)
287 rwerror("SEEK", blk);
919c85da 288 printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
6eb81197 289 for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize)
57195990 290 if (write(fd, cp, (int)dev_bsize) != dev_bsize) {
d72e970b
KM
291 (void)lseek(fd, blk * dev_bsize + i + dev_bsize, 0);
292 printf(" %ld,", blk + i / dev_bsize);
6d8dba44 293 }
cee9cbac
KM
294 printf("\n");
295 return;
5d9906c0
KM
296}
297
3e2a23ca
KM
298/*
299 * allocate a data block with the specified number of fragments
300 */
301allocblk(frags)
569ec282 302 long frags;
3e2a23ca
KM
303{
304 register int i, j, k;
305
306 if (frags <= 0 || frags > sblock.fs_frag)
307 return (0);
569ec282 308 for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) {
3e2a23ca 309 for (j = 0; j <= sblock.fs_frag - frags; j++) {
569ec282 310 if (testbmap(i + j))
3e2a23ca
KM
311 continue;
312 for (k = 1; k < frags; k++)
569ec282 313 if (testbmap(i + j + k))
3e2a23ca
KM
314 break;
315 if (k < frags) {
316 j += k;
317 continue;
318 }
319 for (k = 0; k < frags; k++)
320 setbmap(i + j + k);
321 n_blks += frags;
322 return (i + j);
323 }
324 }
325 return (0);
326}
327
328/*
329 * Free a previously allocated block
330 */
331freeblk(blkno, frags)
332 daddr_t blkno;
569ec282 333 long frags;
3e2a23ca
KM
334{
335 struct inodesc idesc;
336
337 idesc.id_blkno = blkno;
338 idesc.id_numfrags = frags;
d72e970b 339 (void)pass4check(&idesc);
3e2a23ca
KM
340}
341
3ad2f081
KM
342/*
343 * Find a pathname
344 */
345getpathname(namebuf, curdir, ino)
346 char *namebuf;
347 ino_t curdir, ino;
348{
349 int len;
350 register char *cp;
351 struct inodesc idesc;
57195990 352 static int busy = 0;
3ad2f081
KM
353 extern int findname();
354
57195990
KM
355 if (busy ||
356 (statemap[curdir] != DSTATE && statemap[curdir] != DFOUND)) {
d72e970b 357 (void)strcpy(namebuf, "?");
3ad2f081
KM
358 return;
359 }
57195990 360 busy = 1;
569ec282 361 bzero((char *)&idesc, sizeof(struct inodesc));
3ad2f081 362 idesc.id_type = DATA;
57195990 363 idesc.id_fix = IGNORE;
45ae1fd3 364 cp = &namebuf[MAXPATHLEN - 1];
315f1422 365 *cp = '\0';
3ad2f081
KM
366 if (curdir != ino) {
367 idesc.id_parent = curdir;
368 goto namelookup;
369 }
370 while (ino != ROOTINO) {
371 idesc.id_number = ino;
372 idesc.id_func = findino;
373 idesc.id_name = "..";
c7153893 374 if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
3ad2f081
KM
375 break;
376 namelookup:
377 idesc.id_number = idesc.id_parent;
378 idesc.id_parent = ino;
379 idesc.id_func = findname;
380 idesc.id_name = namebuf;
c7153893 381 if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0)
3ad2f081
KM
382 break;
383 len = strlen(namebuf);
384 cp -= len;
d72e970b 385 bcopy(namebuf, cp, (size_t)len);
3ad2f081 386 *--cp = '/';
d21a5ed2
KM
387 if (cp < &namebuf[MAXNAMLEN])
388 break;
3ad2f081
KM
389 ino = idesc.id_number;
390 }
57195990 391 busy = 0;
d21a5ed2
KM
392 if (ino != ROOTINO)
393 *--cp = '?';
d72e970b 394 bcopy(cp, namebuf, (size_t)(&namebuf[MAXPATHLEN] - cp));
3ad2f081
KM
395}
396
392fe950 397void
5d9906c0
KM
398catch()
399{
5d9906c0
KM
400 ckfini();
401 exit(12);
402}
403
3cfe4689
MK
404/*
405 * When preening, allow a single quit to signal
406 * a special exit after filesystem checks complete
407 * so that reboot sequence may be interrupted.
408 */
392fe950 409void
3cfe4689
MK
410catchquit()
411{
412 extern returntosingle;
413
414 printf("returning to single-user after filesystem check\n");
415 returntosingle = 1;
416 (void)signal(SIGQUIT, SIG_DFL);
417}
418
419/*
420 * Ignore a single quit signal; wait and flush just in case.
421 * Used by child processes in preen.
422 */
392fe950 423void
3cfe4689
MK
424voidquit()
425{
426
427 sleep(1);
428 (void)signal(SIGQUIT, SIG_IGN);
429 (void)signal(SIGQUIT, SIG_DFL);
430}
431
5d9906c0
KM
432/*
433 * determine whether an inode should be fixed.
434 */
7718c0e6 435dofix(idesc, msg)
5d9906c0 436 register struct inodesc *idesc;
7718c0e6 437 char *msg;
5d9906c0
KM
438{
439
440 switch (idesc->id_fix) {
441
442 case DONTKNOW:
7718c0e6 443 if (idesc->id_type == DATA)
569ec282 444 direrror(idesc->id_number, msg);
7718c0e6
KM
445 else
446 pwarn(msg);
447 if (preen) {
448 printf(" (SALVAGED)\n");
449 idesc->id_fix = FIX;
450 return (ALTERED);
451 }
5d9906c0
KM
452 if (reply("SALVAGE") == 0) {
453 idesc->id_fix = NOFIX;
454 return (0);
455 }
456 idesc->id_fix = FIX;
457 return (ALTERED);
458
459 case FIX:
460 return (ALTERED);
461
462 case NOFIX:
57195990 463 case IGNORE:
5d9906c0
KM
464 return (0);
465
466 default:
467 errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix);
468 }
469 /* NOTREACHED */
470}
471
5d9906c0
KM
472/* VARARGS1 */
473errexit(s1, s2, s3, s4)
474 char *s1;
475{
7718c0e6 476 printf(s1, s2, s3, s4);
5d9906c0
KM
477 exit(8);
478}
479
480/*
569ec282
KM
481 * An unexpected inconsistency occured.
482 * Die if preening, otherwise just print message and continue.
5d9906c0
KM
483 */
484/* VARARGS1 */
485pfatal(s, a1, a2, a3)
486 char *s;
487{
488
489 if (preen) {
490 printf("%s: ", devname);
491 printf(s, a1, a2, a3);
492 printf("\n");
7718c0e6
KM
493 printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
494 devname);
495 exit(8);
5d9906c0
KM
496 }
497 printf(s, a1, a2, a3);
498}
499
5d9906c0 500/*
569ec282 501 * Pwarn just prints a message when not preening,
5d9906c0
KM
502 * or a warning (preceded by filename) when preening.
503 */
504/* VARARGS1 */
505pwarn(s, a1, a2, a3, a4, a5, a6)
506 char *s;
507{
508
509 if (preen)
510 printf("%s: ", devname);
511 printf(s, a1, a2, a3, a4, a5, a6);
512}
513
514#ifndef lint
515/*
516 * Stub for routines from kernel.
517 */
518panic(s)
519 char *s;
520{
521
7718c0e6
KM
522 pfatal("INTERNAL INCONSISTENCY:");
523 errexit(s);
5d9906c0
KM
524}
525#endif