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