new location for ufs sources
[unix-history] / usr / src / sbin / fsck / utilities.c
CommitLineData
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 8static 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 19long diskreads, totalreads; /* Disk cache statistics */
5d9906c0
KM
20long lseek();
21
22ftypeok(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
42reply(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
68getline(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 */
90bufinit()
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 */
126BUFAREA *
127getdatablk(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 */
143foundit:
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
155BUFAREA *
156getblk(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
176flush(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
201rwerr(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
213ckfini()
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
239bread(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
274bwrite(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 */
307allocblk(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 */
337freeblk(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 */
351getpathname(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
400catch()
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 */
412catchquit()
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 */
425voidquit()
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 436dofix(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 */
473errexit(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 */
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
KM
500/*
501 * Pwarn is like printf when not preening,
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