Commit | Line | Data |
---|---|---|
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 | 9 | static 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 | 23 | long diskreads, totalreads; /* Disk cache statistics */ |
5d9906c0 KM |
24 | |
25 | ftypeok(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 |
46 | reply(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 | */ | |
81 | bufinit() | |
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 | 118 | struct bufarea * |
adc5a10c KM |
119 | getdatablk(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 */ | |
135 | foundit: | |
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 | 147 | void |
5d9906c0 | 148 | getblk(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 |
165 | flush(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 |
190 | rwerror(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 | ||
202 | ckfini() | |
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 |
237 | bread(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 |
275 | bwrite(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 | */ | |
311 | allocblk(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 | */ | |
341 | freeblk(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 | */ | |
355 | getpathname(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 | 411 | void |
5d9906c0 KM |
412 | catch() |
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 | 424 | void |
3cfe4689 MK |
425 | catchquit() |
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 | 438 | void |
3cfe4689 MK |
439 | voidquit() |
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 | 450 | dofix(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 */ |
488 | errexit(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 */ | |
500 | pfatal(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 */ | |
520 | pwarn(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 | */ | |
533 | panic(s) | |
534 | char *s; | |
535 | { | |
536 | ||
7718c0e6 KM |
537 | pfatal("INTERNAL INCONSISTENCY:"); |
538 | errexit(s); | |
5d9906c0 KM |
539 | } |
540 | #endif |