utilities.c revision 1.40 1 /* $NetBSD: utilities.c,v 1.40 2003/07/13 08:16:15 itojun Exp $ */
2
3 /*
4 * Copyright (c) 1980, 1986, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 #ifndef lint
38 #if 0
39 static char sccsid[] = "@(#)utilities.c 8.6 (Berkeley) 5/19/95";
40 #else
41 __RCSID("$NetBSD: utilities.c,v 1.40 2003/07/13 08:16:15 itojun Exp $");
42 #endif
43 #endif /* not lint */
44
45 #include <sys/param.h>
46 #include <sys/time.h>
47
48 #include <ufs/ufs/dinode.h>
49 #include <ufs/ufs/dir.h>
50 #include <ufs/ffs/fs.h>
51 #include <ufs/ffs/ffs_extern.h>
52 #include <ufs/ufs/ufs_bswap.h>
53
54 #include <ctype.h>
55 #include <err.h>
56 #include <errno.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <unistd.h>
61
62 #include "fsutil.h"
63 #include "fsck.h"
64 #include "extern.h"
65
66 long diskreads, totalreads; /* Disk cache statistics */
67
68 static void rwerror __P((char *, daddr_t));
69
70 extern int returntosingle;
71
72 int
73 ftypeok(dp)
74 union dinode *dp;
75 {
76 switch (iswap16(DIP(dp, mode)) & IFMT) {
77
78 case IFDIR:
79 case IFREG:
80 case IFBLK:
81 case IFCHR:
82 case IFLNK:
83 case IFSOCK:
84 case IFIFO:
85 return (1);
86
87 default:
88 if (debug)
89 printf("bad file type 0%o\n", iswap16(DIP(dp, mode)));
90 return (0);
91 }
92 }
93
94 int
95 reply(question)
96 char *question;
97 {
98 int persevere;
99 char c;
100
101 if (preen)
102 pfatal("INTERNAL ERROR: GOT TO reply()");
103 persevere = !strcmp(question, "CONTINUE");
104 printf("\n");
105 if (!persevere && (nflag || fswritefd < 0)) {
106 printf("%s? no\n\n", question);
107 resolved = 0;
108 return (0);
109 }
110 if (yflag || (persevere && nflag)) {
111 printf("%s? yes\n\n", question);
112 return (1);
113 }
114 do {
115 printf("%s? [yn] ", question);
116 (void) fflush(stdout);
117 c = getc(stdin);
118 while (c != '\n' && getc(stdin) != '\n') {
119 if (feof(stdin)) {
120 resolved = 0;
121 return (0);
122 }
123 }
124 } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N');
125 printf("\n");
126 if (c == 'y' || c == 'Y')
127 return (1);
128 resolved = 0;
129 return (0);
130 }
131
132 /*
133 * Malloc buffers and set up cache.
134 */
135 void
136 bufinit()
137 {
138 struct bufarea *bp;
139 long bufcnt, i;
140 char *bufp;
141
142 pbp = pdirbp = (struct bufarea *)0;
143 bufp = malloc((unsigned int)sblock->fs_bsize);
144 if (bufp == 0)
145 errx(EEXIT, "cannot allocate buffer pool");
146 cgblk.b_un.b_buf = bufp;
147 initbarea(&cgblk);
148 bufp = malloc((unsigned int)APPLEUFS_LABEL_SIZE);
149 if (bufp == 0)
150 errx(EEXIT, "cannot allocate buffer pool");
151 appleufsblk.b_un.b_buf = bufp;
152 initbarea(&appleufsblk);
153 bufhead.b_next = bufhead.b_prev = &bufhead;
154 bufcnt = MAXBUFSPACE / sblock->fs_bsize;
155 if (bufcnt < MINBUFS)
156 bufcnt = MINBUFS;
157 for (i = 0; i < bufcnt; i++) {
158 bp = (struct bufarea *)malloc(sizeof(struct bufarea));
159 bufp = malloc((unsigned int)sblock->fs_bsize);
160 if (bp == NULL || bufp == NULL) {
161 if (i >= MINBUFS)
162 break;
163 errx(EEXIT, "cannot allocate buffer pool");
164 }
165 bp->b_un.b_buf = bufp;
166 bp->b_prev = &bufhead;
167 bp->b_next = bufhead.b_next;
168 bufhead.b_next->b_prev = bp;
169 bufhead.b_next = bp;
170 initbarea(bp);
171 }
172 bufhead.b_size = i; /* save number of buffers */
173 }
174
175 /*
176 * Manage a cache of directory blocks.
177 */
178 struct bufarea *
179 getdatablk(blkno, size)
180 daddr_t blkno;
181 long size;
182 {
183 struct bufarea *bp;
184
185 for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next)
186 if (bp->b_bno == fsbtodb(sblock, blkno))
187 goto foundit;
188 for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev)
189 if ((bp->b_flags & B_INUSE) == 0)
190 break;
191 if (bp == &bufhead)
192 errx(EEXIT, "deadlocked buffer pool");
193 getblk(bp, blkno, size);
194 /* fall through */
195 foundit:
196 totalreads++;
197 bp->b_prev->b_next = bp->b_next;
198 bp->b_next->b_prev = bp->b_prev;
199 bp->b_prev = &bufhead;
200 bp->b_next = bufhead.b_next;
201 bufhead.b_next->b_prev = bp;
202 bufhead.b_next = bp;
203 bp->b_flags |= B_INUSE;
204 return (bp);
205 }
206
207 void
208 getblk(bp, blk, size)
209 struct bufarea *bp;
210 daddr_t blk;
211 long size;
212 {
213 daddr_t dblk;
214
215 dblk = fsbtodb(sblock, blk);
216 if (bp->b_bno != dblk) {
217 flush(fswritefd, bp);
218 diskreads++;
219 bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size);
220 bp->b_bno = dblk;
221 bp->b_size = size;
222 }
223 }
224
225 void
226 flush(fd, bp)
227 int fd;
228 struct bufarea *bp;
229 {
230 int i, j;
231 struct csum *ccsp;
232
233 if (!bp->b_dirty)
234 return;
235 if (bp->b_errs != 0)
236 pfatal("WRITING %sZERO'ED BLOCK %lld TO DISK\n",
237 (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ",
238 (long long)bp->b_bno);
239 bp->b_dirty = 0;
240 bp->b_errs = 0;
241 bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
242 if (bp != &sblk)
243 return;
244 for (i = 0, j = 0; i < sblock->fs_cssize; i += sblock->fs_bsize, j++) {
245 int size = sblock->fs_cssize - i < sblock->fs_bsize ?
246 sblock->fs_cssize - i : sblock->fs_bsize;
247 ccsp = (struct csum *)((char *)sblock->fs_csp + i);
248 if (needswap)
249 ffs_csum_swap(ccsp, ccsp, size);
250 bwrite(fswritefd, (char *)ccsp,
251 fsbtodb(sblock, sblock->fs_csaddr + j * sblock->fs_frag),
252 size);
253 if (needswap)
254 ffs_csum_swap(ccsp, ccsp, size);
255 }
256 }
257
258 static void
259 rwerror(mesg, blk)
260 char *mesg;
261 daddr_t blk;
262 {
263
264 if (preen == 0)
265 printf("\n");
266 pfatal("CANNOT %s: BLK %lld", mesg, (long long)blk);
267 if (reply("CONTINUE") == 0)
268 exit(EEXIT);
269 }
270
271 void
272 ckfini()
273 {
274 struct bufarea *bp, *nbp;
275 int ofsmodified, cnt = 0;
276
277 if (fswritefd < 0) {
278 (void)close(fsreadfd);
279 return;
280 }
281 flush(fswritefd, &sblk);
282 if (havesb && bflag != 0 &&
283 !preen && reply("UPDATE STANDARD SUPERBLOCK")) {
284 if (!is_ufs2 && (sblock->fs_old_flags & FS_FLAGS_UPDATED) == 0)
285 sblk.b_bno = SBLOCK_UFS1 / dev_bsize;
286 else
287 sblk.b_bno = sblock->fs_sblockloc / dev_bsize;
288 sbdirty();
289 flush(fswritefd, &sblk);
290 }
291 flush(fswritefd, &appleufsblk);
292 free(appleufsblk.b_un.b_buf);
293 flush(fswritefd, &cgblk);
294 free(cgblk.b_un.b_buf);
295 for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) {
296 cnt++;
297 flush(fswritefd, bp);
298 nbp = bp->b_prev;
299 free(bp->b_un.b_buf);
300 free((char *)bp);
301 }
302 if (bufhead.b_size != cnt)
303 errx(EEXIT, "Panic: lost %d buffers", bufhead.b_size - cnt);
304 pbp = pdirbp = (struct bufarea *)0;
305 if (markclean && (sblock->fs_clean & FS_ISCLEAN) == 0) {
306 /*
307 * Mark the file system as clean, and sync the superblock.
308 */
309 if (preen)
310 pwarn("MARKING FILE SYSTEM CLEAN\n");
311 else if (!reply("MARK FILE SYSTEM CLEAN"))
312 markclean = 0;
313 if (markclean) {
314 sblock->fs_clean = FS_ISCLEAN;
315 sblock->fs_pendingblocks = 0;
316 sblock->fs_pendinginodes = 0;
317 sbdirty();
318 ofsmodified = fsmodified;
319 flush(fswritefd, &sblk);
320 #if LITE2BORKEN
321 fsmodified = ofsmodified;
322 #endif
323 if (!preen)
324 printf(
325 "\n***** FILE SYSTEM MARKED CLEAN *****\n");
326 }
327 }
328 if (debug)
329 printf("cache missed %ld of %ld (%d%%)\n", diskreads,
330 totalreads, (int)(diskreads * 100 / totalreads));
331 (void)close(fsreadfd);
332 (void)close(fswritefd);
333 }
334
335 int
336 bread(fd, buf, blk, size)
337 int fd;
338 char *buf;
339 daddr_t blk;
340 long size;
341 {
342 char *cp;
343 int i, errs;
344 off_t offset;
345
346 offset = blk;
347 offset *= dev_bsize;
348 if (lseek(fd, offset, 0) < 0)
349 rwerror("SEEK", blk);
350 else if (read(fd, buf, (int)size) == size)
351 return (0);
352 rwerror("READ", blk);
353 if (lseek(fd, offset, 0) < 0)
354 rwerror("SEEK", blk);
355 errs = 0;
356 memset(buf, 0, (size_t)size);
357 printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
358 for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
359 if (read(fd, cp, (int)secsize) != secsize) {
360 (void)lseek(fd, offset + i + secsize, 0);
361 if (secsize != dev_bsize && dev_bsize != 1)
362 printf(" %lld (%lld),",
363 (long long)((blk*dev_bsize + i) / secsize),
364 (long long)(blk + i / dev_bsize));
365 else
366 printf(" %lld,",
367 (long long)(blk + i / dev_bsize));
368 errs++;
369 }
370 }
371 printf("\n");
372 return (errs);
373 }
374
375 void
376 bwrite(fd, buf, blk, size)
377 int fd;
378 char *buf;
379 daddr_t blk;
380 long size;
381 {
382 int i;
383 char *cp;
384 off_t offset;
385
386 if (fd < 0)
387 return;
388 offset = blk;
389 offset *= dev_bsize;
390 if (lseek(fd, offset, 0) < 0)
391 rwerror("SEEK", blk);
392 else if (write(fd, buf, (int)size) == size) {
393 fsmodified = 1;
394 return;
395 }
396 rwerror("WRITE", blk);
397 if (lseek(fd, offset, 0) < 0)
398 rwerror("SEEK", blk);
399 printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
400 for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize)
401 if (write(fd, cp, (int)dev_bsize) != dev_bsize) {
402 (void)lseek(fd, offset + i + dev_bsize, 0);
403 printf(" %lld,", (long long)(blk + i / dev_bsize));
404 }
405 printf("\n");
406 return;
407 }
408
409 /*
410 * allocate a data block with the specified number of fragments
411 */
412 daddr_t
413 allocblk(frags)
414 long frags;
415 {
416 int i, j, k, cg, baseblk;
417 struct cg *cgp = cgrp;
418
419 if (frags <= 0 || frags > sblock->fs_frag)
420 return (0);
421 for (i = 0; i < maxfsblock - sblock->fs_frag; i += sblock->fs_frag) {
422 for (j = 0; j <= sblock->fs_frag - frags; j++) {
423 if (testbmap(i + j))
424 continue;
425 for (k = 1; k < frags; k++)
426 if (testbmap(i + j + k))
427 break;
428 if (k < frags) {
429 j += k;
430 continue;
431 }
432 cg = dtog(sblock, i + j);
433 getblk(&cgblk, cgtod(sblock, cg), sblock->fs_cgsize);
434 memcpy(cgp, cgblk.b_un.b_cg, sblock->fs_cgsize);
435 if ((doswap && !needswap) || (!doswap && needswap))
436 ffs_cg_swap(cgblk.b_un.b_cg, cgp, sblock);
437 if (!cg_chkmagic(cgp, 0))
438 pfatal("CG %d: ALLOCBLK: BAD MAGIC NUMBER\n",
439 cg);
440 baseblk = dtogd(sblock, i + j);
441 for (k = 0; k < frags; k++) {
442 setbmap(i + j + k);
443 clrbit(cg_blksfree(cgp, 0), baseblk + k);
444 }
445 n_blks += frags;
446 if (frags == sblock->fs_frag)
447 cgp->cg_cs.cs_nbfree--;
448 else
449 cgp->cg_cs.cs_nffree -= frags;
450 cgdirty();
451 return (i + j);
452 }
453 }
454 return (0);
455 }
456
457 /*
458 * Free a previously allocated block
459 */
460 void
461 freeblk(blkno, frags)
462 daddr_t blkno;
463 long frags;
464 {
465 struct inodesc idesc;
466
467 idesc.id_blkno = blkno;
468 idesc.id_numfrags = frags;
469 (void)pass4check(&idesc);
470 }
471
472 /*
473 * Find a pathname
474 */
475 void
476 getpathname(namebuf, namebuflen, curdir, ino)
477 char *namebuf;
478 size_t namebuflen;
479 ino_t curdir, ino;
480 {
481 int len;
482 char *cp;
483 struct inodesc idesc;
484 static int busy = 0;
485 struct inostat *info;
486
487 if (curdir == ino && ino == ROOTINO) {
488 (void)strlcpy(namebuf, "/", namebuflen);
489 return;
490 }
491 info = inoinfo(curdir);
492 if (busy || (info->ino_state != DSTATE && info->ino_state != DFOUND)) {
493 (void)strlcpy(namebuf, "?", namebuflen);
494 return;
495 }
496 busy = 1;
497 memset(&idesc, 0, sizeof(struct inodesc));
498 idesc.id_type = DATA;
499 idesc.id_fix = IGNORE;
500 cp = &namebuf[MAXPATHLEN - 1];
501 *cp = '\0';
502 if (curdir != ino) {
503 idesc.id_parent = curdir;
504 goto namelookup;
505 }
506 while (ino != ROOTINO) {
507 idesc.id_number = ino;
508 idesc.id_func = findino;
509 idesc.id_name = "..";
510 if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
511 break;
512 namelookup:
513 idesc.id_number = idesc.id_parent;
514 idesc.id_parent = ino;
515 idesc.id_func = findname;
516 idesc.id_name = namebuf;
517 if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0)
518 break;
519 len = strlen(namebuf);
520 cp -= len;
521 memmove(cp, namebuf, (size_t)len);
522 *--cp = '/';
523 if (cp < &namebuf[MAXNAMLEN])
524 break;
525 ino = idesc.id_number;
526 }
527 busy = 0;
528 if (ino != ROOTINO)
529 *--cp = '?';
530 memmove(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp));
531 }
532
533 void
534 catch(sig)
535 int sig;
536 {
537 if (!doinglevel2) {
538 markclean = 0;
539 ckfini();
540 }
541 exit(12);
542 }
543
544 /*
545 * When preening, allow a single quit to signal
546 * a special exit after filesystem checks complete
547 * so that reboot sequence may be interrupted.
548 */
549 void
550 catchquit(sig)
551 int sig;
552 {
553 int errsave = errno;
554
555 printf("returning to single-user after file system check\n");
556 returntosingle = 1;
557 (void)signal(SIGQUIT, SIG_DFL);
558 errno = errsave;
559 }
560
561 /*
562 * Ignore a single quit signal; wait and flush just in case.
563 * Used by child processes in preen.
564 */
565 void
566 voidquit(sig)
567 int sig;
568 {
569 int errsave = errno;
570
571 sleep(1);
572 (void)signal(SIGQUIT, SIG_IGN);
573 (void)signal(SIGQUIT, SIG_DFL);
574 errno = errsave;
575 }
576
577 /*
578 * determine whether an inode should be fixed.
579 */
580 int
581 dofix(idesc, msg)
582 struct inodesc *idesc;
583 char *msg;
584 {
585
586 switch (idesc->id_fix) {
587
588 case DONTKNOW:
589 if (idesc->id_type == DATA)
590 direrror(idesc->id_number, msg);
591 else
592 pwarn("%s", msg);
593 if (preen) {
594 printf(" (SALVAGED)\n");
595 idesc->id_fix = FIX;
596 return (ALTERED);
597 }
598 if (reply("SALVAGE") == 0) {
599 idesc->id_fix = NOFIX;
600 return (0);
601 }
602 idesc->id_fix = FIX;
603 return (ALTERED);
604
605 case FIX:
606 return (ALTERED);
607
608 case NOFIX:
609 case IGNORE:
610 return (0);
611
612 default:
613 errx(EEXIT, "UNKNOWN INODESC FIX MODE %d", idesc->id_fix);
614 }
615 /* NOTREACHED */
616 return (0);
617 }
618
619 void
620 copyback_cg(blk)
621 struct bufarea *blk;
622 {
623
624 memcpy(blk->b_un.b_cg, cgrp, sblock->fs_cgsize);
625 if (needswap)
626 ffs_cg_swap(cgrp, blk->b_un.b_cg, sblock);
627 }
628
629 void
630 infohandler(int sig)
631 {
632 got_siginfo = 1;
633 }
634
635 /*
636 * Look up state information for an inode.
637 */
638 struct inostat *
639 inoinfo(ino_t inum)
640 {
641 static struct inostat unallocated = { USTATE, 0, 0 };
642 struct inostatlist *ilp;
643 int iloff;
644
645 if (inum > maxino)
646 errx(EEXIT, "inoinfo: inumber %d out of range", inum);
647 ilp = &inostathead[inum / sblock->fs_ipg];
648 iloff = inum % sblock->fs_ipg;
649 if (iloff >= ilp->il_numalloced)
650 return (&unallocated);
651 return (&ilp->il_stat[iloff]);
652 }
653
654 void
655 sb_oldfscompat_write(struct fs *fs)
656 {
657 if (fs->fs_magic != FS_UFS1_MAGIC)
658 return;
659
660 fs->fs_old_time = fs->fs_time;
661 fs->fs_old_cstotal.cs_ndir = fs->fs_cstotal.cs_ndir;
662 fs->fs_old_cstotal.cs_nbfree = fs->fs_cstotal.cs_nbfree;
663 fs->fs_old_cstotal.cs_nifree = fs->fs_cstotal.cs_nifree;
664 fs->fs_old_cstotal.cs_nffree = fs->fs_cstotal.cs_nffree;
665 }
666