1/*	$NetBSD: utilities.c,v 1.71 2023/07/05 10:59:08 riastradh 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
35static char sccsid[] = "@(#)utilities.c	8.6 (Berkeley) 5/19/95";
36#else
37__RCSID("$NetBSD: utilities.c,v 1.71 2023/07/05 10:59:08 riastradh 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#include <ufs/ufs/quota2.h>
50
51#include <ctype.h>
52#include <err.h>
53#include <errno.h>
54#include <stdio.h>
55#include <stdlib.h>
56#include <string.h>
57#include <unistd.h>
58#include <signal.h>
59
60#include "fsutil.h"
61#include "fsck.h"
62#include "extern.h"
63#include "exitvalues.h"
64
65long	diskreads, totalreads;	/* Disk cache statistics */
66
67static void rwerror(const char *, daddr_t);
68
69int
70ftypeok(union dinode *dp)
71{
72	switch (iswap16(DIP(dp, mode)) & IFMT) {
73
74	case IFDIR:
75	case IFREG:
76	case IFBLK:
77	case IFCHR:
78	case IFLNK:
79	case IFSOCK:
80	case IFIFO:
81		return (1);
82
83	default:
84		if (debug)
85			printf("bad file type 0%o\n", iswap16(DIP(dp, mode)));
86		return (0);
87	}
88}
89
90int
91reply(const char *question)
92{
93	int persevere;
94	char c;
95
96	if (preen)
97		pfatal("INTERNAL ERROR: GOT TO reply()");
98	persevere = !strcmp(question, "CONTINUE");
99	printf("\n");
100	if (!persevere && (nflag || fswritefd < 0)) {
101		printf("%s? no\n\n", question);
102		resolved = 0;
103		return (0);
104	}
105	if (yflag || (persevere && nflag)) {
106		printf("%s? yes\n\n", question);
107		return (1);
108	}
109	do	{
110		printf("%s? [yn] ", question);
111		(void) fflush(stdout);
112		c = getc(stdin);
113		while (c != '\n' && getc(stdin) != '\n') {
114			if (feof(stdin)) {
115				resolved = 0;
116				return (0);
117			}
118		}
119	} while (c != 'y' && c != 'Y' && c != 'n' && c != 'N');
120	printf("\n");
121	if (c == 'y' || c == 'Y')
122		return (1);
123	resolved = 0;
124	return (0);
125}
126
127/*
128 * Malloc buffers and set up cache.
129 */
130void
131bufinit(void)
132{
133	struct bufarea *bp;
134	long bufcnt, i;
135	char *bufp;
136
137	pbp = pdirbp = (struct bufarea *)0;
138	bufp = aligned_alloc(DEV_BSIZE, (unsigned int)sblock->fs_bsize);
139	if (bufp == 0)
140		errexit("cannot allocate buffer pool");
141	cgblk.b_un.b_buf = bufp;
142	initbarea(&cgblk);
143#ifndef NO_APPLE_UFS
144	bufp = aligned_alloc(DEV_BSIZE, (unsigned int)APPLEUFS_LABEL_SIZE);
145	if (bufp == 0)
146		errexit("cannot allocate buffer pool");
147	appleufsblk.b_un.b_buf = bufp;
148	initbarea(&appleufsblk);
149#endif
150	bufhead.b_next = bufhead.b_prev = &bufhead;
151	bufcnt = MAXBUFSPACE / sblock->fs_bsize;
152	if (bufcnt < MINBUFS)
153		bufcnt = MINBUFS;
154	for (i = 0; i < bufcnt; i++) {
155		bp = malloc(sizeof(struct bufarea));
156		bufp = aligned_alloc(DEV_BSIZE, (unsigned int)sblock->fs_bsize);
157		if (bp == NULL || bufp == NULL) {
158			if (i >= MINBUFS) {
159				if (bp)
160					free(bp);
161				if (bufp)
162					free(bufp);
163				break;
164			}
165			errexit("cannot allocate buffer pool");
166		}
167		bp->b_un.b_buf = bufp;
168		bp->b_prev = &bufhead;
169		bp->b_next = bufhead.b_next;
170		bufhead.b_next->b_prev = bp;
171		bufhead.b_next = bp;
172		initbarea(bp);
173	}
174	bufhead.b_size = i;	/* save number of buffers */
175}
176
177/*
178 * Manage a cache of directory blocks.
179 */
180struct bufarea *
181getdatablk(daddr_t blkno, 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 == FFS_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		errexit("deadlocked buffer pool");
193	/* fall through */
194foundit:
195	getblk(bp, blkno, size);
196	bp->b_prev->b_next = bp->b_next;
197	bp->b_next->b_prev = bp->b_prev;
198	bp->b_prev = &bufhead;
199	bp->b_next = bufhead.b_next;
200	bufhead.b_next->b_prev = bp;
201	bufhead.b_next = bp;
202	bp->b_flags |= B_INUSE;
203	return (bp);
204}
205
206void
207getblk(struct bufarea *bp, daddr_t blk, long size)
208{
209	daddr_t dblk;
210
211	dblk = FFS_FSBTODB(sblock, blk);
212	totalreads++;
213	if (bp->b_bno != dblk) {
214		flush(fswritefd, bp);
215		diskreads++;
216		bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size);
217		bp->b_bno = dblk;
218		bp->b_size = size;
219	}
220}
221
222void
223flush(int fd, struct bufarea *bp)
224{
225	int i, j;
226	struct csum *ccsp;
227
228	if (!bp->b_dirty)
229		return;
230	if (bp->b_errs != 0)
231		pfatal("WRITING %sZERO'ED BLOCK %lld TO DISK\n",
232		    (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ",
233		    (long long)bp->b_bno);
234	bp->b_dirty = 0;
235	bp->b_errs = 0;
236	bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
237	if (bp != &sblk)
238		return;
239	for (i = 0, j = 0; i < sblock->fs_cssize; i += sblock->fs_bsize, j++) {
240		int size = sblock->fs_cssize - i < sblock->fs_bsize ?
241			sblock->fs_cssize - i : sblock->fs_bsize;
242		ccsp = (struct csum *)((char *)sblock->fs_csp + i);
243		if (needswap)
244			ffs_csum_swap(ccsp, ccsp, size);
245		bwrite(fswritefd, (char *)ccsp,
246		    FFS_FSBTODB(sblock, sblock->fs_csaddr + j * sblock->fs_frag),
247		    size);
248		if (needswap)
249			ffs_csum_swap(ccsp, ccsp, size);
250	}
251}
252
253static void
254rwerror(const char *mesg, daddr_t blk)
255{
256
257	if (preen == 0)
258		printf("\n");
259	pfatal("CANNOT %s: BLK %lld", mesg, (long long)blk);
260	if (reply("CONTINUE") == 0)
261		exit(FSCK_EXIT_CHECK_FAILED);
262}
263
264void
265ckfini(int noint)
266{
267	struct bufarea *bp, *nbp;
268	int cnt = 0;
269
270	if (!noint) {
271		if (doinglevel2)
272			return;
273		markclean = 0;
274	}
275
276	if (fswritefd < 0) {
277		(void)close(fsreadfd);
278		return;
279	}
280	flush(fswritefd, &sblk);
281	if (havesb && bflag != 0 &&
282	    (preen || reply("UPDATE STANDARD SUPERBLOCK"))) {
283		if (preen)
284			pwarn("UPDATING STANDARD SUPERBLOCK\n");
285		if (!is_ufs2 && (sblock->fs_old_flags & FS_FLAGS_UPDATED) == 0)
286			sblk.b_bno = SBLOCK_UFS1 / dev_bsize;
287		else
288			sblk.b_bno = sblock->fs_sblockloc / dev_bsize;
289		sbdirty();
290		flush(fswritefd, &sblk);
291	}
292#ifndef NO_APPLE_UFS
293	flush(fswritefd, &appleufsblk);
294	free(appleufsblk.b_un.b_buf);
295#endif
296	flush(fswritefd, &cgblk);
297	free(cgblk.b_un.b_buf);
298	for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) {
299		cnt++;
300		flush(fswritefd, bp);
301		nbp = bp->b_prev;
302		free(bp->b_un.b_buf);
303		free((char *)bp);
304	}
305	if (bufhead.b_size != cnt)
306		errexit("Panic: lost %d buffers", bufhead.b_size - cnt);
307	pbp = pdirbp = (struct bufarea *)0;
308	if (markclean && (sblock->fs_clean & FS_ISCLEAN) == 0) {
309		/*
310		 * Mark the file system as clean, and sync the superblock.
311		 */
312		if (preen)
313			pwarn("MARKING FILE SYSTEM CLEAN\n");
314		else if (!reply("MARK FILE SYSTEM CLEAN"))
315			markclean = 0;
316		if (markclean) {
317			sblock->fs_clean = FS_ISCLEAN;
318			sblock->fs_pendingblocks = 0;
319			sblock->fs_pendinginodes = 0;
320			sbdirty();
321			flush(fswritefd, &sblk);
322			if (!preen)
323				printf(
324				    "\n***** FILE SYSTEM MARKED CLEAN *****\n");
325		}
326	}
327	if (doing2ea) {
328		printf("ENABLING EXTATTR SUPPORT\n");
329		is_ufs2ea = 1;
330		sbdirty();
331		flush(fswritefd, &sblk);
332	}
333	if (doing2noea) {
334		printf("DISABLING EXTATTR SUPPORT\n");
335		is_ufs2ea = 0;
336		sbdirty();
337		flush(fswritefd, &sblk);
338	}
339	if (debug)
340		printf("cache missed %ld of %ld (%d%%)\n", diskreads,
341		    totalreads, (int)(diskreads * 100 / totalreads));
342	cleanup_wapbl();
343	(void)close(fsreadfd);
344	(void)close(fswritefd);
345}
346
347int
348bread(int fd, char *buf, daddr_t blk, long size)
349{
350	char *cp;
351	int i, errs;
352	off_t offset;
353
354	offset = blk;
355	offset *= dev_bsize;
356	if ((pread(fd, buf, (int)size, offset) == size) &&
357	    read_wapbl(buf, size, blk) == 0)
358		return (0);
359	rwerror("READ", blk);
360	errs = 0;
361	memset(buf, 0, (size_t)size);
362	printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
363	for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
364		if (pread(fd, cp, (int)secsize, offset + i) != secsize) {
365			if (secsize != dev_bsize && dev_bsize != 1)
366				printf(" %lld (%lld),",
367				    (long long)((blk*dev_bsize + i) / secsize),
368				    (long long)(blk + i / dev_bsize));
369			else
370				printf(" %lld,",
371				    (long long)(blk + i / dev_bsize));
372			errs++;
373		}
374	}
375	printf("\n");
376	return (errs);
377}
378
379void
380bwrite(int fd, char *buf, daddr_t blk, 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 (pwrite(fd, buf, (int)size, offset) == size) {
391		fsmodified = 1;
392		return;
393	}
394	rwerror("WRITE", blk);
395	printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
396	for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize)
397		if (pwrite(fd, cp, (int)dev_bsize, offset + i) != dev_bsize)
398			printf(" %lld,", (long long)(blk + i / dev_bsize));
399	printf("\n");
400	return;
401}
402
403/*
404 * allocate a data block with the specified number of fragments
405 */
406daddr_t
407allocblk(long frags)
408{
409	int i, j, k, cg, baseblk;
410	struct cg *cgp = cgrp;
411
412	if (frags <= 0 || frags > sblock->fs_frag)
413		return (0);
414	for (i = 0; i < maxfsblock - sblock->fs_frag; i += sblock->fs_frag) {
415		for (j = 0; j <= sblock->fs_frag - frags; j++) {
416			if (testbmap(i + j))
417				continue;
418			for (k = 1; k < frags; k++)
419				if (testbmap(i + j + k))
420					break;
421			if (k < frags) {
422				j += k;
423				continue;
424			}
425			cg = dtog(sblock, i + j);
426			getblk(&cgblk, cgtod(sblock, cg), sblock->fs_cgsize);
427			memcpy(cgp, cgblk.b_un.b_cg, sblock->fs_cgsize);
428			if ((doswap && !needswap) || (!doswap && needswap))
429				ffs_cg_swap(cgblk.b_un.b_cg, cgp, sblock);
430			if (!cg_chkmagic(cgp, 0))
431				pfatal("CG %d: ALLOCBLK: BAD MAGIC NUMBER\n",
432				    cg);
433			baseblk = dtogd(sblock, i + j);
434			for (k = 0; k < frags; k++) {
435				setbmap(i + j + k);
436				clrbit(cg_blksfree(cgp, 0), baseblk + k);
437			}
438			n_blks += frags;
439			if (frags == sblock->fs_frag) {
440				cgp->cg_cs.cs_nbfree--;
441				sblock->fs_cstotal.cs_nbfree--;
442				sblock->fs_cs(fs, cg).cs_nbfree--;
443				ffs_clusteracct(sblock, cgp,
444				    ffs_fragstoblks(sblock, baseblk), -1);
445			} else {
446				cgp->cg_cs.cs_nffree -= frags;
447				sblock->fs_cstotal.cs_nffree -= frags;
448				sblock->fs_cs(fs, cg).cs_nffree -= frags;
449			}
450			sbdirty();
451			cgdirty();
452			return (i + j);
453		}
454	}
455	return (0);
456}
457
458/*
459 * Free a previously allocated block
460 */
461void
462freeblk(daddr_t blkno, long frags)
463{
464	struct inodesc idesc;
465
466	memset(&idesc, 0, sizeof(idesc));
467	idesc.id_blkno = blkno;
468	idesc.id_numfrags = frags;
469	(void)pass4check(&idesc);
470}
471
472/*
473 * Find a pathname
474 */
475void
476getpathname(char *namebuf, size_t namebuflen, ino_t curdir, ino_t ino)
477{
478	int len;
479	char *cp;
480	struct inodesc idesc;
481	static int busy = 0;
482	struct inostat *info;
483
484	if (curdir == ino && ino == UFS_ROOTINO) {
485		(void)strlcpy(namebuf, "/", namebuflen);
486		return;
487	}
488	info = inoinfo(curdir);
489	if (busy || (info->ino_state != DSTATE && info->ino_state != DFOUND)) {
490		(void)strlcpy(namebuf, "?", namebuflen);
491		return;
492	}
493	busy = 1;
494	memset(&idesc, 0, sizeof(struct inodesc));
495	idesc.id_type = DATA;
496	idesc.id_fix = IGNORE;
497	cp = &namebuf[MAXPATHLEN - 1];
498	*cp = '\0';
499	if (curdir != ino) {
500		idesc.id_parent = curdir;
501		goto namelookup;
502	}
503	while (ino != UFS_ROOTINO) {
504		idesc.id_number = ino;
505		idesc.id_func = findino;
506		idesc.id_name = "..";
507		if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
508			break;
509	namelookup:
510		idesc.id_number = idesc.id_parent;
511		idesc.id_parent = ino;
512		idesc.id_func = findname;
513		idesc.id_name = namebuf;
514		if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0)
515			break;
516		len = strlen(namebuf);
517		cp -= len;
518		memmove(cp, namebuf, (size_t)len);
519		*--cp = '/';
520		if (cp < &namebuf[FFS_MAXNAMLEN])
521			break;
522		ino = idesc.id_number;
523	}
524	busy = 0;
525	if (ino != UFS_ROOTINO)
526		*--cp = '?';
527	memmove(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp));
528}
529
530/*
531 * determine whether an inode should be fixed.
532 */
533int
534dofix(struct inodesc *idesc, const char *msg)
535{
536
537	switch (idesc->id_fix) {
538
539	case DONTKNOW:
540		if (idesc->id_type == DATA)
541			direrror(idesc->id_number, msg);
542		else
543			pwarn("%s", msg);
544		if (preen) {
545			printf(" (SALVAGED)\n");
546			idesc->id_fix = FIX;
547			return (ALTERED);
548		}
549		if (reply("SALVAGE") == 0) {
550			idesc->id_fix = NOFIX;
551			return (0);
552		}
553		idesc->id_fix = FIX;
554		return (ALTERED);
555
556	case FIX:
557		return (ALTERED);
558
559	case NOFIX:
560	case IGNORE:
561		return (0);
562
563	default:
564		errexit("UNKNOWN INODESC FIX MODE %d", idesc->id_fix);
565	}
566	/* NOTREACHED */
567	return (0);
568}
569
570void
571copyback_cg(struct bufarea *blk)
572{
573
574	memcpy(blk->b_un.b_cg, cgrp, sblock->fs_cgsize);
575	if (needswap)
576		ffs_cg_swap(cgrp, blk->b_un.b_cg, sblock);
577}
578
579void
580infohandler(int sig)
581{
582	got_siginfo = 1;
583}
584
585/*
586 * Look up state information for an inode.
587 */
588struct inostat *
589inoinfo(ino_t inum)
590{
591	static struct inostat unallocated = { USTATE, 0, 0 };
592	struct inostatlist *ilp;
593	size_t iloff;
594
595	if (inum > maxino)
596		errexit("inoinfo: inumber %llu out of range",
597		    (unsigned long long)inum);
598	ilp = &inostathead[inum / sblock->fs_ipg];
599	iloff = inum % sblock->fs_ipg;
600	if (iloff >= ilp->il_numalloced)
601		return (&unallocated);
602	return (&ilp->il_stat[iloff]);
603}
604
605void
606sb_oldfscompat_read(struct fs *fs, struct fs **fssave)
607{
608	if ((fs->fs_magic != FS_UFS1_MAGIC) ||
609	    (fs->fs_old_flags & FS_FLAGS_UPDATED))
610		return;
611
612	/* Save a copy of fields that may be modified for compatibility */
613	if (fssave) {
614		if (!*fssave)
615			*fssave = malloc(sizeof(struct fs));
616		if (!*fssave)
617			errexit("cannot allocate space for compat store");
618		memmove(*fssave, fs, sizeof(struct fs));
619
620		if (debug)
621			printf("detected ufs1 superblock not yet updated for ufs2 kernels\n");
622
623		if (doswap) {
624			uint16_t postbl[256];
625			int i, n;
626
627			if (fs->fs_old_postblformat == FS_42POSTBLFMT)
628				n = 256;
629			else
630				n = 128;
631
632			/* extract the postbl from the unswapped superblock */
633			if (!needswap)
634				ffs_sb_swap(*fssave, *fssave);
635			memmove(postbl, (&(*fssave)->fs_old_postbl_start),
636			    n * sizeof(postbl[0]));
637			if (!needswap)
638				ffs_sb_swap(*fssave, *fssave);
639
640			/* Now swap it */
641			for (i=0; i < n; i++)
642				postbl[i] = bswap16(postbl[i]);
643
644			/* And put it back such that it will get correctly
645			 * unscrambled if it is swapped again on the way out
646			 */
647			if (needswap)
648				ffs_sb_swap(*fssave, *fssave);
649			memmove((&(*fssave)->fs_old_postbl_start), postbl,
650			    n * sizeof(postbl[0]));
651			if (needswap)
652				ffs_sb_swap(*fssave, *fssave);
653		}
654
655	}
656
657	/* These fields will be overwritten by their
658	 * original values in fs_oldfscompat_write, so it is harmless
659	 * to modify them here.
660	 */
661	fs->fs_cstotal.cs_ndir =
662	    fs->fs_old_cstotal.cs_ndir;
663	fs->fs_cstotal.cs_nbfree =
664	    fs->fs_old_cstotal.cs_nbfree;
665	fs->fs_cstotal.cs_nifree =
666	    fs->fs_old_cstotal.cs_nifree;
667	fs->fs_cstotal.cs_nffree =
668	    fs->fs_old_cstotal.cs_nffree;
669
670	fs->fs_maxbsize = fs->fs_bsize;
671	fs->fs_time = fs->fs_old_time;
672	fs->fs_size = fs->fs_old_size;
673	fs->fs_dsize = fs->fs_old_dsize;
674	fs->fs_csaddr = fs->fs_old_csaddr;
675	fs->fs_sblockloc = SBLOCK_UFS1;
676
677	fs->fs_flags = fs->fs_old_flags;
678
679	if (fs->fs_old_postblformat == FS_42POSTBLFMT) {
680		fs->fs_old_nrpos = 8;
681		fs->fs_old_npsect = fs->fs_old_nsect;
682		fs->fs_old_interleave = 1;
683		fs->fs_old_trackskew = 0;
684	}
685}
686
687void
688sb_oldfscompat_write(struct fs *fs, struct fs *fssave)
689{
690	if ((fs->fs_magic != FS_UFS1_MAGIC) ||
691	    (fs->fs_old_flags & FS_FLAGS_UPDATED))
692		return;
693
694	fs->fs_old_flags = fs->fs_flags;
695	fs->fs_old_time = fs->fs_time;
696	fs->fs_old_cstotal.cs_ndir = fs->fs_cstotal.cs_ndir;
697	fs->fs_old_cstotal.cs_nbfree = fs->fs_cstotal.cs_nbfree;
698	fs->fs_old_cstotal.cs_nifree = fs->fs_cstotal.cs_nifree;
699	fs->fs_old_cstotal.cs_nffree = fs->fs_cstotal.cs_nffree;
700
701	fs->fs_flags = fssave->fs_flags;
702
703	if (fs->fs_old_postblformat == FS_42POSTBLFMT) {
704		fs->fs_old_nrpos = fssave->fs_old_nrpos;
705		fs->fs_old_npsect = fssave->fs_old_npsect;
706		fs->fs_old_interleave = fssave->fs_old_interleave;
707		fs->fs_old_trackskew = fssave->fs_old_trackskew;
708	}
709
710	memmove(&fs->fs_old_postbl_start, &fssave->fs_old_postbl_start,
711	    ((fs->fs_old_postblformat == FS_42POSTBLFMT) ?
712	    512 : 256));
713}
714
715struct uquot *
716find_uquot(struct uquot_hash *uq_hash, uint32_t uid, int alloc)
717{
718	struct uquot *uq;
719	SLIST_FOREACH(uq, &uq_hash[uid & q2h_hash_mask], uq_entries) {
720		if (uq->uq_uid == uid)
721			return uq;
722	}
723	if (!alloc)
724		return NULL;
725	uq = malloc(sizeof(struct uquot));
726	if (uq == NULL)
727		errexit("cannot allocate quota entry");
728	memset(uq, 0, sizeof(struct uquot));
729	uq->uq_uid = uid;
730	SLIST_INSERT_HEAD(&uq_hash[uid & q2h_hash_mask], uq, uq_entries);
731	return uq;
732}
733
734void
735remove_uquot(struct uquot_hash *uq_hash, struct uquot *uq)
736{
737	SLIST_REMOVE(&uq_hash[uq->uq_uid & q2h_hash_mask],
738	    uq, uquot, uq_entries);
739}
740
741void
742update_uquot(ino_t inum, uid_t uid, gid_t gid, int64_t bchange, int64_t ichange)
743{
744	/* simple uquot cache: remember the last used */
745	static struct uquot *uq_u = NULL;
746	static struct uquot *uq_g = NULL;
747
748	if (inum < UFS_ROOTINO)
749		return;
750	if (is_journal_inode(inum))
751		return;
752	if (is_quota_inode(inum))
753		return;
754
755	if (uquot_user_hash == NULL)
756		return;
757
758	if (uq_u == NULL || uq_u->uq_uid != uid)
759		uq_u = find_uquot(uquot_user_hash, uid, 1);
760	uq_u->uq_b += bchange;
761	uq_u->uq_i += ichange;
762	if (uq_g == NULL || uq_g->uq_uid != gid)
763		uq_g = find_uquot(uquot_group_hash, gid, 1);
764	uq_g->uq_b += bchange;
765	uq_g->uq_i += ichange;
766}
767
768int
769is_quota_inode(ino_t inum)
770{
771
772	if ((sblock->fs_flags & FS_DOQUOTA2) == 0)
773		return 0;
774
775	if (sblock->fs_quota_magic != Q2_HEAD_MAGIC)
776		return 0;
777
778	if (sblock->fs_quotafile[USRQUOTA] == inum)
779		return 1;
780
781	if (sblock->fs_quotafile[GRPQUOTA] == inum)
782		return 1;
783
784	return 0;
785}
786