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