convert to BSD only copyright
[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 *
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
19static 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 30long diskreads, totalreads; /* Disk cache statistics */
5d9906c0 31long lseek();
d35d4c2b 32char *malloc();
5d9906c0
KM
33
34ftypeok(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
54reply(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 */
89bufinit()
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 125struct bufarea *
adc5a10c
KM
126getdatablk(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 */
142foundit:
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 154struct bufarea *
5d9906c0 155getblk(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
173flush(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
198rwerror(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
210ckfini()
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
240bread(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
275bwrite(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 */
308allocblk(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 */
338freeblk(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 */
352getpathname(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 401void
5d9906c0
KM
402catch()
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 413void
3cfe4689
MK
414catchquit()
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 427void
3cfe4689
MK
428voidquit()
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 439dofix(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 */
476errexit(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 */
488pfatal(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 */
508pwarn(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 */
521panic(s)
522 char *s;
523{
524
7718c0e6
KM
525 pfatal("INTERNAL INCONSISTENCY:");
526 errexit(s);
5d9906c0
KM
527}
528#endif