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