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