169800Stomsoft/*
2234846Strasz * Copyright (c) 1980, 1989, 1993 The Regents of the University of California.
369800Stomsoft * Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz
4234846Strasz * Copyright (c) 2012 The FreeBSD Foundation
569800Stomsoft * All rights reserved.
6114067Sschweikh *
769800Stomsoft * This code is derived from software contributed to Berkeley by
869800Stomsoft * Christoph Herrmann and Thomas-Henning von Kamptz, Munich and Frankfurt.
9114067Sschweikh *
10234846Strasz * Portions of this software were developed by Edward Tomasz Napierala
11234846Strasz * under sponsorship from the FreeBSD Foundation.
12234846Strasz *
1369800Stomsoft * Redistribution and use in source and binary forms, with or without
1469800Stomsoft * modification, are permitted provided that the following conditions
1569800Stomsoft * are met:
1669800Stomsoft * 1. Redistributions of source code must retain the above copyright
1769800Stomsoft *    notice, this list of conditions and the following disclaimer.
1869800Stomsoft * 2. Redistributions in binary form must reproduce the above copyright
1969800Stomsoft *    notice, this list of conditions and the following disclaimer in the
2069800Stomsoft *    documentation and/or other materials provided with the distribution.
2169800Stomsoft * 3. All advertising materials mentioning features or use of this software
2269800Stomsoft *    must display the following acknowledgment:
2369800Stomsoft *      This product includes software developed by the University of
2469800Stomsoft *      California, Berkeley and its contributors, as well as Christoph
2569800Stomsoft *      Herrmann and Thomas-Henning von Kamptz.
2669800Stomsoft * 4. Neither the name of the University nor the names of its contributors
2769800Stomsoft *    may be used to endorse or promote products derived from this software
2869800Stomsoft *    without specific prior written permission.
29114067Sschweikh *
3069800Stomsoft * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
3169800Stomsoft * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
3269800Stomsoft * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3369800Stomsoft * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3469800Stomsoft * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3569800Stomsoft * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3669800Stomsoft * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3769800Stomsoft * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3869800Stomsoft * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3969800Stomsoft * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
4069800Stomsoft * SUCH DAMAGE.
4169800Stomsoft *
4269926Stomsoft * $TSHeader: src/sbin/growfs/growfs.c,v 1.5 2000/12/12 19:31:00 tomsoft Exp $
4369800Stomsoft *
4469800Stomsoft */
4569800Stomsoft
4669800Stomsoft#ifndef lint
4769800Stomsoftstatic const char copyright[] =
4869800Stomsoft"@(#) Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz\n\
4969800StomsoftCopyright (c) 1980, 1989, 1993 The Regents of the University of California.\n\
5069800StomsoftAll rights reserved.\n";
5169800Stomsoft#endif /* not lint */
5269800Stomsoft
53140351Scharnier#include <sys/cdefs.h>
54140351Scharnier__FBSDID("$FreeBSD$");
5569800Stomsoft
5669800Stomsoft#include <sys/param.h>
5769800Stomsoft#include <sys/ioctl.h>
5869800Stomsoft#include <sys/stat.h>
59114936Sgrog#include <sys/disk.h>
60234846Strasz#include <sys/ucred.h>
61234846Strasz#include <sys/mount.h>
6269800Stomsoft
6369800Stomsoft#include <stdio.h>
6469800Stomsoft#include <paths.h>
6569800Stomsoft#include <ctype.h>
6669800Stomsoft#include <err.h>
6769800Stomsoft#include <fcntl.h>
68234846Strasz#include <fstab.h>
69234846Strasz#include <inttypes.h>
70103949Smike#include <limits.h>
71234846Strasz#include <mntopts.h>
72243246Strasz#include <paths.h>
7369800Stomsoft#include <stdlib.h>
74127798Sle#include <stdint.h>
7569800Stomsoft#include <string.h>
76127821Sbde#include <time.h>
7769800Stomsoft#include <unistd.h>
7869800Stomsoft#include <ufs/ufs/dinode.h>
7969800Stomsoft#include <ufs/ffs/fs.h>
80234846Strasz#include <libutil.h>
8169800Stomsoft
8269800Stomsoft#include "debug.h"
8369800Stomsoft
8469800Stomsoft#ifdef FS_DEBUG
8569800Stomsoftint	_dbg_lvl_ = (DL_INFO);	/* DL_TRC */
8669800Stomsoft#endif /* FS_DEBUG */
8769800Stomsoft
8869800Stomsoftstatic union {
8969800Stomsoft	struct fs	fs;
90234189Strasz	char		pad[SBLOCKSIZE];
9169800Stomsoft} fsun1, fsun2;
9269800Stomsoft#define	sblock	fsun1.fs	/* the new superblock */
9369800Stomsoft#define	osblock	fsun2.fs	/* the old superblock */
9469800Stomsoft
9598542Smckusick/*
9698542Smckusick * Possible superblock locations ordered from most to least likely.
9798542Smckusick */
9898542Smckusickstatic int sblock_try[] = SBLOCKSEARCH;
9998542Smckusickstatic ufs2_daddr_t sblockloc;
10098542Smckusick
10169800Stomsoftstatic union {
10269800Stomsoft	struct cg	cg;
103234189Strasz	char		pad[MAXBSIZE];
10469800Stomsoft} cgun1, cgun2;
10569800Stomsoft#define	acg	cgun1.cg	/* a cylinder cgroup (new) */
10669800Stomsoft#define	aocg	cgun2.cg	/* an old cylinder group */
10769800Stomsoft
10898542Smckusickstatic struct csum	*fscs;	/* cylinder summary */
10969800Stomsoft
11077885Stomsoftstatic void	growfs(int, int, unsigned int);
11198542Smckusickstatic void	rdfs(ufs2_daddr_t, size_t, void *, int);
11298542Smckusickstatic void	wtfs(ufs2_daddr_t, size_t, void *, int, unsigned int);
11369800Stomsoftstatic int	charsperline(void);
11469926Stomsoftstatic void	usage(void);
11569800Stomsoftstatic int	isblock(struct fs *, unsigned char *, int);
11669800Stomsoftstatic void	clrblock(struct fs *, unsigned char *, int);
11769800Stomsoftstatic void	setblock(struct fs *, unsigned char *, int);
11877885Stomsoftstatic void	initcg(int, time_t, int, unsigned int);
11977885Stomsoftstatic void	updjcg(int, time_t, int, int, unsigned int);
12077885Stomsoftstatic void	updcsloc(time_t, int, int, unsigned int);
12198542Smckusickstatic void	frag_adjust(ufs2_daddr_t, int);
12269800Stomsoftstatic void	updclst(int);
123234846Straszstatic void	mount_reload(const struct statfs *stfs);
12469800Stomsoft
12569800Stomsoft/*
126223652Strasz * Here we actually start growing the file system. We basically read the
127114067Sschweikh * cylinder summary from the first cylinder group as we want to update
128114067Sschweikh * this on the fly during our various operations. First we handle the
12969800Stomsoft * changes in the former last cylinder group. Afterwards we create all new
130114067Sschweikh * cylinder groups.  Now we handle the cylinder group containing the
131114067Sschweikh * cylinder summary which might result in a relocation of the whole
132114067Sschweikh * structure.  In the end we write back the updated cylinder summary, the
13369800Stomsoft * new superblock, and slightly patched versions of the super block
13469800Stomsoft * copies.
13569800Stomsoft */
13669800Stomsoftstatic void
13777885Stomsoftgrowfs(int fsi, int fso, unsigned int Nflag)
13869800Stomsoft{
13969800Stomsoft	DBG_FUNC("growfs")
140232548Strasz	time_t modtime;
141232548Strasz	uint cylno;
142232548Strasz	int i, j, width;
143232548Strasz	char tmpbuf[100];
14469800Stomsoft
14569800Stomsoft	DBG_ENTER;
14669800Stomsoft
147217726Smarcel	time(&modtime);
14869800Stomsoft
14969800Stomsoft	/*
15069800Stomsoft	 * Get the cylinder summary into the memory.
15169800Stomsoft	 */
15277885Stomsoft	fscs = (struct csum *)calloc((size_t)1, (size_t)sblock.fs_cssize);
153232548Strasz	if (fscs == NULL)
15469926Stomsoft		errx(1, "calloc failed");
15569800Stomsoft	for (i = 0; i < osblock.fs_cssize; i += osblock.fs_bsize) {
15669800Stomsoft		rdfs(fsbtodb(&osblock, osblock.fs_csaddr +
15777885Stomsoft		    numfrags(&osblock, i)), (size_t)MIN(osblock.fs_cssize - i,
158232548Strasz		    osblock.fs_bsize), (void *)(((char *)fscs) + i), fsi);
15969800Stomsoft	}
16069800Stomsoft
16169800Stomsoft#ifdef FS_DEBUG
162232548Strasz	{
163232548Strasz		struct csum *dbg_csp;
164232548Strasz		int dbg_csc;
165232548Strasz		char dbg_line[80];
16669800Stomsoft
167232548Strasz		dbg_csp = fscs;
168232548Strasz
169232548Strasz		for (dbg_csc = 0; dbg_csc < osblock.fs_ncg; dbg_csc++) {
170232548Strasz			snprintf(dbg_line, sizeof(dbg_line),
171232548Strasz			    "%d. old csum in old location", dbg_csc);
172232548Strasz			DBG_DUMP_CSUM(&osblock, dbg_line, dbg_csp++);
173232548Strasz		}
17469800Stomsoft	}
17569800Stomsoft#endif /* FS_DEBUG */
17669800Stomsoft	DBG_PRINT0("fscs read\n");
17769800Stomsoft
17869800Stomsoft	/*
17969800Stomsoft	 * Do all needed changes in the former last cylinder group.
18069800Stomsoft	 */
181232548Strasz	updjcg(osblock.fs_ncg - 1, modtime, fsi, fso, Nflag);
18269800Stomsoft
18369800Stomsoft	/*
184223652Strasz	 * Dump out summary information about file system.
18569800Stomsoft	 */
186234846Strasz#ifdef FS_DEBUG
187234314Strasz#define B2MBFACTOR (1 / (1024.0 * 1024.0))
188127816Smux	printf("growfs: %.1fMB (%jd sectors) block size %d, fragment size %d\n",
18969800Stomsoft	    (float)sblock.fs_size * sblock.fs_fsize * B2MBFACTOR,
190127816Smux	    (intmax_t)fsbtodb(&sblock, sblock.fs_size), sblock.fs_bsize,
191127816Smux	    sblock.fs_fsize);
19298542Smckusick	printf("\tusing %d cylinder groups of %.2fMB, %d blks, %d inodes.\n",
19398542Smckusick	    sblock.fs_ncg, (float)sblock.fs_fpg * sblock.fs_fsize * B2MBFACTOR,
19498542Smckusick	    sblock.fs_fpg / sblock.fs_frag, sblock.fs_ipg);
19598542Smckusick	if (sblock.fs_flags & FS_DOSOFTDEP)
19698542Smckusick		printf("\twith soft updates\n");
197234314Strasz#undef B2MBFACTOR
198234846Strasz#endif /* FS_DEBUG */
19969800Stomsoft
20069800Stomsoft	/*
20169800Stomsoft	 * Now build the cylinders group blocks and
20269800Stomsoft	 * then print out indices of cylinder groups.
20369800Stomsoft	 */
204261963Sbrueffer	printf("super-block backups (for fsck_ffs -b #) at:\n");
20569800Stomsoft	i = 0;
20669800Stomsoft	width = charsperline();
20769800Stomsoft
20869800Stomsoft	/*
20969800Stomsoft	 * Iterate for only the new cylinder groups.
21069800Stomsoft	 */
21169800Stomsoft	for (cylno = osblock.fs_ncg; cylno < sblock.fs_ncg; cylno++) {
212217726Smarcel		initcg(cylno, modtime, fso, Nflag);
213174706Sdas		j = sprintf(tmpbuf, " %jd%s",
214174706Sdas		    (intmax_t)fsbtodb(&sblock, cgsblock(&sblock, cylno)),
215232548Strasz		    cylno < (sblock.fs_ncg - 1) ? "," : "" );
21669800Stomsoft		if (i + j >= width) {
21769800Stomsoft			printf("\n");
21869800Stomsoft			i = 0;
21969800Stomsoft		}
22069800Stomsoft		i += j;
22169800Stomsoft		printf("%s", tmpbuf);
22269800Stomsoft		fflush(stdout);
22369800Stomsoft	}
22469800Stomsoft	printf("\n");
22569800Stomsoft
22669800Stomsoft	/*
22769800Stomsoft	 * Do all needed changes in the first cylinder group.
22869800Stomsoft	 * allocate blocks in new location
22969800Stomsoft	 */
230217726Smarcel	updcsloc(modtime, fsi, fso, Nflag);
23169800Stomsoft
23269800Stomsoft	/*
23369800Stomsoft	 * Now write the cylinder summary back to disk.
23469800Stomsoft	 */
23569800Stomsoft	for (i = 0; i < sblock.fs_cssize; i += sblock.fs_bsize) {
23669800Stomsoft		wtfs(fsbtodb(&sblock, sblock.fs_csaddr + numfrags(&sblock, i)),
23777885Stomsoft		    (size_t)MIN(sblock.fs_cssize - i, sblock.fs_bsize),
23877885Stomsoft		    (void *)(((char *)fscs) + i), fso, Nflag);
23969800Stomsoft	}
24069800Stomsoft	DBG_PRINT0("fscs written\n");
24169800Stomsoft
24269800Stomsoft#ifdef FS_DEBUG
243232548Strasz	{
244232548Strasz		struct csum	*dbg_csp;
245232548Strasz		int	dbg_csc;
246232548Strasz		char	dbg_line[80];
24769800Stomsoft
248232548Strasz		dbg_csp = fscs;
249232548Strasz		for (dbg_csc = 0; dbg_csc < sblock.fs_ncg; dbg_csc++) {
250232548Strasz			snprintf(dbg_line, sizeof(dbg_line),
251232548Strasz			    "%d. new csum in new location", dbg_csc);
252232548Strasz			DBG_DUMP_CSUM(&sblock, dbg_line, dbg_csp++);
253232548Strasz		}
25469800Stomsoft	}
25569800Stomsoft#endif /* FS_DEBUG */
25669800Stomsoft
25769800Stomsoft	/*
25869800Stomsoft	 * Now write the new superblock back to disk.
25969800Stomsoft	 */
260217726Smarcel	sblock.fs_time = modtime;
26198542Smckusick	wtfs(sblockloc, (size_t)SBLOCKSIZE, (void *)&sblock, fso, Nflag);
26269800Stomsoft	DBG_PRINT0("sblock written\n");
263232548Strasz	DBG_DUMP_FS(&sblock, "new initial sblock");
26469800Stomsoft
26569800Stomsoft	/*
26669800Stomsoft	 * Clean up the dynamic fields in our superblock copies.
26769800Stomsoft	 */
26869800Stomsoft	sblock.fs_fmod = 0;
26969800Stomsoft	sblock.fs_clean = 1;
27069800Stomsoft	sblock.fs_ronly = 0;
27169800Stomsoft	sblock.fs_cgrotor = 0;
27269800Stomsoft	sblock.fs_state = 0;
27369800Stomsoft	memset((void *)&sblock.fs_fsmnt, 0, sizeof(sblock.fs_fsmnt));
27469800Stomsoft	sblock.fs_flags &= FS_DOSOFTDEP;
27569800Stomsoft
27669800Stomsoft	/*
27769800Stomsoft	 * XXX
278114067Sschweikh	 * The following fields are currently distributed from the superblock
27969800Stomsoft	 * to the copies:
28069800Stomsoft	 *     fs_minfree
28169800Stomsoft	 *     fs_rotdelay
28269800Stomsoft	 *     fs_maxcontig
28369800Stomsoft	 *     fs_maxbpg
28469800Stomsoft	 *     fs_minfree,
28569800Stomsoft	 *     fs_optim
28669800Stomsoft	 *     fs_flags regarding SOFTPDATES
28769800Stomsoft	 *
28869800Stomsoft	 * We probably should rather change the summary for the cylinder group
28969800Stomsoft	 * statistics here to the value of what would be in there, if the file
290114067Sschweikh	 * system were created initially with the new size. Therefor we still
29169800Stomsoft	 * need to find an easy way of calculating that.
29269800Stomsoft	 * Possibly we can try to read the first superblock copy and apply the
293114067Sschweikh	 * "diffed" stats between the old and new superblock by still copying
29469800Stomsoft	 * certain parameters onto that.
29569800Stomsoft	 */
29669800Stomsoft
29769800Stomsoft	/*
29869800Stomsoft	 * Write out the duplicate super blocks.
29969800Stomsoft	 */
30069800Stomsoft	for (cylno = 0; cylno < sblock.fs_ncg; cylno++) {
30169800Stomsoft		wtfs(fsbtodb(&sblock, cgsblock(&sblock, cylno)),
30298542Smckusick		    (size_t)SBLOCKSIZE, (void *)&sblock, fso, Nflag);
30369800Stomsoft	}
30469800Stomsoft	DBG_PRINT0("sblock copies written\n");
305232548Strasz	DBG_DUMP_FS(&sblock, "new other sblocks");
30669800Stomsoft
30769800Stomsoft	DBG_LEAVE;
30869800Stomsoft	return;
30969800Stomsoft}
31069800Stomsoft
31169800Stomsoft/*
312114067Sschweikh * This creates a new cylinder group structure, for more details please see
313114067Sschweikh * the source of newfs(8), as this function is taken over almost unchanged.
314114067Sschweikh * As this is never called for the first cylinder group, the special
31569800Stomsoft * provisions for that case are removed here.
31669800Stomsoft */
31769800Stomsoftstatic void
318217726Smarcelinitcg(int cylno, time_t modtime, int fso, unsigned int Nflag)
31969800Stomsoft{
32069800Stomsoft	DBG_FUNC("initcg")
321212839Sbrian	static caddr_t iobuf;
322203770Smckusick	long blkno, start;
323241013Smdf	ino_t ino;
324127798Sle	ufs2_daddr_t i, cbase, dmax;
32598542Smckusick	struct ufs1_dinode *dp1;
32692806Sobrien	struct csum *cs;
327234312Strasz	uint j, d, dupper, dlower;
32869800Stomsoft
329212839Sbrian	if (iobuf == NULL && (iobuf = malloc(sblock.fs_bsize * 3)) == NULL)
33098542Smckusick		errx(37, "panic: cannot allocate I/O buffer");
331212839Sbrian
33269800Stomsoft	/*
33369800Stomsoft	 * Determine block bounds for cylinder group.
33498542Smckusick	 * Allow space for super block summary information in first
33598542Smckusick	 * cylinder group.
33669800Stomsoft	 */
33769800Stomsoft	cbase = cgbase(&sblock, cylno);
33869800Stomsoft	dmax = cbase + sblock.fs_fpg;
33998542Smckusick	if (dmax > sblock.fs_size)
34069800Stomsoft		dmax = sblock.fs_size;
34169800Stomsoft	dlower = cgsblock(&sblock, cylno) - cbase;
34269800Stomsoft	dupper = cgdmin(&sblock, cylno) - cbase;
34398542Smckusick	if (cylno == 0)	/* XXX fscs may be relocated */
34469800Stomsoft		dupper += howmany(sblock.fs_cssize, sblock.fs_fsize);
34598542Smckusick	cs = &fscs[cylno];
34698542Smckusick	memset(&acg, 0, sblock.fs_cgsize);
347217726Smarcel	acg.cg_time = modtime;
34869800Stomsoft	acg.cg_magic = CG_MAGIC;
34969800Stomsoft	acg.cg_cgx = cylno;
35069800Stomsoft	acg.cg_niblk = sblock.fs_ipg;
351212839Sbrian	acg.cg_initediblk = sblock.fs_ipg < 2 * INOPB(&sblock) ?
352212839Sbrian	    sblock.fs_ipg : 2 * INOPB(&sblock);
35369800Stomsoft	acg.cg_ndblk = dmax - cbase;
35498542Smckusick	if (sblock.fs_contigsumsize > 0)
35569800Stomsoft		acg.cg_nclusterblks = acg.cg_ndblk / sblock.fs_frag;
35698542Smckusick	start = &acg.cg_space[0] - (u_char *)(&acg.cg_firstfield);
35798542Smckusick	if (sblock.fs_magic == FS_UFS2_MAGIC) {
35898542Smckusick		acg.cg_iusedoff = start;
35998542Smckusick	} else {
36098542Smckusick		acg.cg_old_ncyl = sblock.fs_old_cpg;
36198542Smckusick		acg.cg_old_time = acg.cg_time;
36298542Smckusick		acg.cg_time = 0;
36398542Smckusick		acg.cg_old_niblk = acg.cg_niblk;
36498542Smckusick		acg.cg_niblk = 0;
365212839Sbrian		acg.cg_initediblk = 0;
36698542Smckusick		acg.cg_old_btotoff = start;
36798542Smckusick		acg.cg_old_boff = acg.cg_old_btotoff +
36898542Smckusick		    sblock.fs_old_cpg * sizeof(int32_t);
36998542Smckusick		acg.cg_iusedoff = acg.cg_old_boff +
37098542Smckusick		    sblock.fs_old_cpg * sizeof(u_int16_t);
37169800Stomsoft	}
372103949Smike	acg.cg_freeoff = acg.cg_iusedoff + howmany(sblock.fs_ipg, CHAR_BIT);
373103949Smike	acg.cg_nextfreeoff = acg.cg_freeoff + howmany(sblock.fs_fpg, CHAR_BIT);
37498542Smckusick	if (sblock.fs_contigsumsize > 0) {
37569800Stomsoft		acg.cg_clustersumoff =
37698542Smckusick		    roundup(acg.cg_nextfreeoff, sizeof(u_int32_t));
37798542Smckusick		acg.cg_clustersumoff -= sizeof(u_int32_t);
37869800Stomsoft		acg.cg_clusteroff = acg.cg_clustersumoff +
37969800Stomsoft		    (sblock.fs_contigsumsize + 1) * sizeof(u_int32_t);
38098542Smckusick		acg.cg_nextfreeoff = acg.cg_clusteroff +
381103949Smike		    howmany(fragstoblks(&sblock, sblock.fs_fpg), CHAR_BIT);
38269800Stomsoft	}
383203770Smckusick	if (acg.cg_nextfreeoff > (unsigned)sblock.fs_cgsize) {
38469800Stomsoft		/*
38598542Smckusick		 * This should never happen as we would have had that panic
386223652Strasz		 * already on file system creation
38769800Stomsoft		 */
38869926Stomsoft		errx(37, "panic: cylinder group too big");
38969800Stomsoft	}
39069800Stomsoft	acg.cg_cs.cs_nifree += sblock.fs_ipg;
39169800Stomsoft	if (cylno == 0)
392241013Smdf		for (ino = 0; ino < ROOTINO; ino++) {
393241013Smdf			setbit(cg_inosused(&acg), ino);
39469800Stomsoft			acg.cg_cs.cs_nifree--;
39569800Stomsoft		}
396136289Sscottl	/*
397223652Strasz	 * For the old file system, we have to initialize all the inodes.
398136289Sscottl	 */
399136289Sscottl	if (sblock.fs_magic == FS_UFS1_MAGIC) {
400136289Sscottl		bzero(iobuf, sblock.fs_bsize);
401203835Sgavin		for (i = 0; i < sblock.fs_ipg / INOPF(&sblock);
402232548Strasz		    i += sblock.fs_frag) {
403212886Smarcel			dp1 = (struct ufs1_dinode *)(void *)iobuf;
404201401Sgavin			for (j = 0; j < INOPB(&sblock); j++) {
405249500Sdelphij				dp1->di_gen = arc4random();
406201401Sgavin				dp1++;
407201401Sgavin			}
408136289Sscottl			wtfs(fsbtodb(&sblock, cgimin(&sblock, cylno) + i),
409136289Sscottl			    sblock.fs_bsize, iobuf, fso, Nflag);
410136289Sscottl		}
41169800Stomsoft	}
41298542Smckusick	if (cylno > 0) {
41398542Smckusick		/*
41498542Smckusick		 * In cylno 0, beginning space is reserved
41598542Smckusick		 * for boot and super blocks.
41698542Smckusick		 */
41798542Smckusick		for (d = 0; d < dlower; d += sblock.fs_frag) {
41898542Smckusick			blkno = d / sblock.fs_frag;
41998542Smckusick			setblock(&sblock, cg_blksfree(&acg), blkno);
42098542Smckusick			if (sblock.fs_contigsumsize > 0)
42198542Smckusick				setbit(cg_clustersfree(&acg), blkno);
42298542Smckusick			acg.cg_cs.cs_nbfree++;
42369800Stomsoft		}
42498542Smckusick		sblock.fs_dsize += dlower;
42569800Stomsoft	}
42669800Stomsoft	sblock.fs_dsize += acg.cg_ndblk - dupper;
42769800Stomsoft	if ((i = dupper % sblock.fs_frag)) {
42869800Stomsoft		acg.cg_frsum[sblock.fs_frag - i]++;
42969800Stomsoft		for (d = dupper + sblock.fs_frag - i; dupper < d; dupper++) {
43069800Stomsoft			setbit(cg_blksfree(&acg), dupper);
43169800Stomsoft			acg.cg_cs.cs_nffree++;
43269800Stomsoft		}
43369800Stomsoft	}
43498542Smckusick	for (d = dupper; d + sblock.fs_frag <= acg.cg_ndblk;
435232548Strasz	    d += sblock.fs_frag) {
43669800Stomsoft		blkno = d / sblock.fs_frag;
43769800Stomsoft		setblock(&sblock, cg_blksfree(&acg), blkno);
43898542Smckusick		if (sblock.fs_contigsumsize > 0)
43969800Stomsoft			setbit(cg_clustersfree(&acg), blkno);
44069800Stomsoft		acg.cg_cs.cs_nbfree++;
44169800Stomsoft	}
44298542Smckusick	if (d < acg.cg_ndblk) {
44398542Smckusick		acg.cg_frsum[acg.cg_ndblk - d]++;
44498542Smckusick		for (; d < acg.cg_ndblk; d++) {
44569800Stomsoft			setbit(cg_blksfree(&acg), d);
44669800Stomsoft			acg.cg_cs.cs_nffree++;
44769800Stomsoft		}
44869800Stomsoft	}
44969800Stomsoft	if (sblock.fs_contigsumsize > 0) {
45098542Smckusick		int32_t *sump = cg_clustersum(&acg);
45198542Smckusick		u_char *mapp = cg_clustersfree(&acg);
45298542Smckusick		int map = *mapp++;
45398542Smckusick		int bit = 1;
45498542Smckusick		int run = 0;
45569800Stomsoft
45669800Stomsoft		for (i = 0; i < acg.cg_nclusterblks; i++) {
45798542Smckusick			if ((map & bit) != 0)
45869800Stomsoft				run++;
45998542Smckusick			else if (run != 0) {
46098542Smckusick				if (run > sblock.fs_contigsumsize)
46169800Stomsoft					run = sblock.fs_contigsumsize;
46269800Stomsoft				sump[run]++;
46369800Stomsoft				run = 0;
46469800Stomsoft			}
465103949Smike			if ((i & (CHAR_BIT - 1)) != CHAR_BIT - 1)
46669800Stomsoft				bit <<= 1;
46798542Smckusick			else {
46869800Stomsoft				map = *mapp++;
46969800Stomsoft				bit = 1;
47069800Stomsoft			}
47169800Stomsoft		}
47269800Stomsoft		if (run != 0) {
47398542Smckusick			if (run > sblock.fs_contigsumsize)
47469800Stomsoft				run = sblock.fs_contigsumsize;
47569800Stomsoft			sump[run]++;
47669800Stomsoft		}
47769800Stomsoft	}
47869800Stomsoft	sblock.fs_cstotal.cs_ndir += acg.cg_cs.cs_ndir;
47969800Stomsoft	sblock.fs_cstotal.cs_nffree += acg.cg_cs.cs_nffree;
48069800Stomsoft	sblock.fs_cstotal.cs_nbfree += acg.cg_cs.cs_nbfree;
48169800Stomsoft	sblock.fs_cstotal.cs_nifree += acg.cg_cs.cs_nifree;
48269800Stomsoft	*cs = acg.cg_cs;
483212839Sbrian
484212839Sbrian	memcpy(iobuf, &acg, sblock.fs_cgsize);
485212839Sbrian	memset(iobuf + sblock.fs_cgsize, '\0',
486212839Sbrian	    sblock.fs_bsize * 3 - sblock.fs_cgsize);
487212839Sbrian
48869800Stomsoft	wtfs(fsbtodb(&sblock, cgtod(&sblock, cylno)),
489212839Sbrian	    sblock.fs_bsize * 3, iobuf, fso, Nflag);
490212839Sbrian	DBG_DUMP_CG(&sblock, "new cg", &acg);
49169800Stomsoft
49269800Stomsoft	DBG_LEAVE;
49369800Stomsoft	return;
49469800Stomsoft}
49569800Stomsoft
49669800Stomsoft/*
497114067Sschweikh * Here we add or subtract (sign +1/-1) the available fragments in a given
49869800Stomsoft * block to or from the fragment statistics. By subtracting before and adding
499114067Sschweikh * after an operation on the free frag map we can easy update the fragment
500108470Sschweikh * statistic, which seems to be otherwise a rather complex operation.
50169800Stomsoft */
50269800Stomsoftstatic void
50398542Smckusickfrag_adjust(ufs2_daddr_t frag, int sign)
50469800Stomsoft{
50569800Stomsoft	DBG_FUNC("frag_adjust")
50669800Stomsoft	int fragsize;
50769800Stomsoft	int f;
50869800Stomsoft
50969800Stomsoft	DBG_ENTER;
51069800Stomsoft
511234314Strasz	fragsize = 0;
51269800Stomsoft	/*
51369800Stomsoft	 * Here frag only needs to point to any fragment in the block we want
51469800Stomsoft	 * to examine.
51569800Stomsoft	 */
516232548Strasz	for (f = rounddown(frag, sblock.fs_frag);
517232548Strasz	    f < roundup(frag + 1, sblock.fs_frag); f++) {
51869800Stomsoft		/*
519114067Sschweikh		 * Count contiguous free fragments.
52069800Stomsoft		 */
521232548Strasz		if (isset(cg_blksfree(&acg), f)) {
52269800Stomsoft			fragsize++;
52369800Stomsoft		} else {
524232548Strasz			if (fragsize && fragsize < sblock.fs_frag) {
52569800Stomsoft				/*
52669800Stomsoft				 * We found something in between.
52769800Stomsoft				 */
528234189Strasz				acg.cg_frsum[fragsize] += sign;
52969926Stomsoft				DBG_PRINT2("frag_adjust [%d]+=%d\n",
530232548Strasz				    fragsize, sign);
53169800Stomsoft			}
532232548Strasz			fragsize = 0;
53369800Stomsoft		}
53469800Stomsoft	}
535232548Strasz	if (fragsize && fragsize < sblock.fs_frag) {
53669800Stomsoft		/*
53769800Stomsoft		 * We found something.
53869800Stomsoft		 */
539232548Strasz		acg.cg_frsum[fragsize] += sign;
540232548Strasz		DBG_PRINT2("frag_adjust [%d]+=%d\n", fragsize, sign);
54169800Stomsoft	}
542232548Strasz	DBG_PRINT2("frag_adjust [[%d]]+=%d\n", fragsize, sign);
54369800Stomsoft
54469800Stomsoft	DBG_LEAVE;
54569800Stomsoft	return;
54669800Stomsoft}
54769800Stomsoft
54869800Stomsoft/*
54969800Stomsoft * Here we do all needed work for the former last cylinder group. It has to be
550223652Strasz * changed in any case, even if the file system ended exactly on the end of
551114067Sschweikh * this group, as there is some slightly inconsistent handling of the number
552114067Sschweikh * of cylinders in the cylinder group. We start again by reading the cylinder
55369800Stomsoft * group from disk. If the last block was not fully available, we first handle
554114067Sschweikh * the missing fragments, then we handle all new full blocks in that file
555114067Sschweikh * system and finally we handle the new last fragmented block in the file
556114067Sschweikh * system.  We again have to handle the fragment statistics rotational layout
55769800Stomsoft * tables and cluster summary during all those operations.
55869800Stomsoft */
55969800Stomsoftstatic void
560217726Smarcelupdjcg(int cylno, time_t modtime, int fsi, int fso, unsigned int Nflag)
56169800Stomsoft{
56269800Stomsoft	DBG_FUNC("updjcg")
563232548Strasz	ufs2_daddr_t cbase, dmax, dupper;
564232548Strasz	struct csum *cs;
565232548Strasz	int i, k;
566232548Strasz	int j = 0;
56769800Stomsoft
56869800Stomsoft	DBG_ENTER;
56969800Stomsoft
57069800Stomsoft	/*
57169800Stomsoft	 * Read the former last (joining) cylinder group from disk, and make
57269800Stomsoft	 * a copy.
57369800Stomsoft	 */
57477885Stomsoft	rdfs(fsbtodb(&osblock, cgtod(&osblock, cylno)),
57577885Stomsoft	    (size_t)osblock.fs_cgsize, (void *)&aocg, fsi);
57669800Stomsoft	DBG_PRINT0("jcg read\n");
577232548Strasz	DBG_DUMP_CG(&sblock, "old joining cg", &aocg);
57869800Stomsoft
57969800Stomsoft	memcpy((void *)&cgun1, (void *)&cgun2, sizeof(cgun2));
58069800Stomsoft
58169800Stomsoft	/*
582114067Sschweikh	 * If the cylinder group had already its new final size almost
58369800Stomsoft	 * nothing is to be done ... except:
58469800Stomsoft	 * For some reason the value of cg_ncyl in the last cylinder group has
585114067Sschweikh	 * to be zero instead of fs_cpg. As this is now no longer the last
58669800Stomsoft	 * cylinder group we have to change that value now to fs_cpg.
587114067Sschweikh	 */
58869800Stomsoft
589232548Strasz	if (cgbase(&osblock, cylno + 1) == osblock.fs_size) {
59098542Smckusick		if (sblock.fs_magic == FS_UFS1_MAGIC)
591234314Strasz			acg.cg_old_ncyl = sblock.fs_old_cpg;
59269800Stomsoft
59377885Stomsoft		wtfs(fsbtodb(&sblock, cgtod(&sblock, cylno)),
59477885Stomsoft		    (size_t)sblock.fs_cgsize, (void *)&acg, fso, Nflag);
59569800Stomsoft		DBG_PRINT0("jcg written\n");
596232548Strasz		DBG_DUMP_CG(&sblock, "new joining cg", &acg);
59769800Stomsoft
59869800Stomsoft		DBG_LEAVE;
59969800Stomsoft		return;
60069800Stomsoft	}
60169800Stomsoft
60269800Stomsoft	/*
60369800Stomsoft	 * Set up some variables needed later.
60469800Stomsoft	 */
60569800Stomsoft	cbase = cgbase(&sblock, cylno);
60669800Stomsoft	dmax = cbase + sblock.fs_fpg;
60769800Stomsoft	if (dmax > sblock.fs_size)
60869800Stomsoft		dmax = sblock.fs_size;
60969800Stomsoft	dupper = cgdmin(&sblock, cylno) - cbase;
610232548Strasz	if (cylno == 0) /* XXX fscs may be relocated */
61169800Stomsoft		dupper += howmany(sblock.fs_cssize, sblock.fs_fsize);
61269800Stomsoft
61369800Stomsoft	/*
61469800Stomsoft	 * Set pointer to the cylinder summary for our cylinder group.
61569800Stomsoft	 */
61669800Stomsoft	cs = fscs + cylno;
61769800Stomsoft
61869800Stomsoft	/*
61969800Stomsoft	 * Touch the cylinder group, update all fields in the cylinder group as
62069800Stomsoft	 * needed, update the free space in the superblock.
62169800Stomsoft	 */
622217726Smarcel	acg.cg_time = modtime;
623203770Smckusick	if ((unsigned)cylno == sblock.fs_ncg - 1) {
62469800Stomsoft		/*
62569800Stomsoft		 * This is still the last cylinder group.
62669800Stomsoft		 */
62798542Smckusick		if (sblock.fs_magic == FS_UFS1_MAGIC)
62898542Smckusick			acg.cg_old_ncyl =
62998542Smckusick			    sblock.fs_old_ncyl % sblock.fs_old_cpg;
63069800Stomsoft	} else {
63198542Smckusick		acg.cg_old_ncyl = sblock.fs_old_cpg;
63269800Stomsoft	}
633232548Strasz	DBG_PRINT2("jcg dbg: %d %u", cylno, sblock.fs_ncg);
634127798Sle#ifdef FS_DEBUG
63598542Smckusick	if (sblock.fs_magic == FS_UFS1_MAGIC)
636232548Strasz		DBG_PRINT2("%d %u", acg.cg_old_ncyl, sblock.fs_old_cpg);
637127798Sle#endif
63898542Smckusick	DBG_PRINT0("\n");
63969800Stomsoft	acg.cg_ndblk = dmax - cbase;
640232548Strasz	sblock.fs_dsize += acg.cg_ndblk - aocg.cg_ndblk;
641232548Strasz	if (sblock.fs_contigsumsize > 0)
64269800Stomsoft		acg.cg_nclusterblks = acg.cg_ndblk / sblock.fs_frag;
64369800Stomsoft
64469800Stomsoft	/*
645114067Sschweikh	 * Now we have to update the free fragment bitmap for our new free
646114067Sschweikh	 * space.  There again we have to handle the fragmentation and also
647114067Sschweikh	 * the rotational layout tables and the cluster summary.  This is
648114067Sschweikh	 * also done per fragment for the first new block if the old file
649114067Sschweikh	 * system end was not on a block boundary, per fragment for the new
650223652Strasz	 * last block if the new file system end is not on a block boundary,
65169800Stomsoft	 * and per block for all space in between.
65269800Stomsoft	 *
65369800Stomsoft	 * Handle the first new block here if it was partially available
65469800Stomsoft	 * before.
65569800Stomsoft	 */
656232548Strasz	if (osblock.fs_size % sblock.fs_frag) {
657232548Strasz		if (roundup(osblock.fs_size, sblock.fs_frag) <=
658232548Strasz		    sblock.fs_size) {
65969800Stomsoft			/*
66069800Stomsoft			 * The new space is enough to fill at least this
66169800Stomsoft			 * block
66269800Stomsoft			 */
663232548Strasz			j = 0;
664232548Strasz			for (i = roundup(osblock.fs_size - cbase,
665232548Strasz			    sblock.fs_frag) - 1; i >= osblock.fs_size - cbase;
66669800Stomsoft			    i--) {
66769800Stomsoft				setbit(cg_blksfree(&acg), i);
66869800Stomsoft				acg.cg_cs.cs_nffree++;
66969800Stomsoft				j++;
67069800Stomsoft			}
67169800Stomsoft
67269800Stomsoft			/*
673114067Sschweikh			 * Check if the fragment just created could join an
67469800Stomsoft			 * already existing fragment at the former end of the
675223652Strasz			 * file system.
67669800Stomsoft			 */
677232548Strasz			if (isblock(&sblock, cg_blksfree(&acg),
678232548Strasz			    ((osblock.fs_size - cgbase(&sblock, cylno)) /
679232548Strasz			     sblock.fs_frag))) {
68069800Stomsoft				/*
681114067Sschweikh				 * The block is now completely available.
68269800Stomsoft				 */
68369800Stomsoft				DBG_PRINT0("block was\n");
684232548Strasz				acg.cg_frsum[osblock.fs_size % sblock.fs_frag]--;
68569800Stomsoft				acg.cg_cs.cs_nbfree++;
686232548Strasz				acg.cg_cs.cs_nffree -= sblock.fs_frag;
687232548Strasz				k = rounddown(osblock.fs_size - cbase,
68869800Stomsoft				    sblock.fs_frag);
689232548Strasz				updclst((osblock.fs_size - cbase) /
690232548Strasz				    sblock.fs_frag);
69169800Stomsoft			} else {
69269800Stomsoft				/*
69369800Stomsoft				 * Lets rejoin a possible partially growed
69469800Stomsoft				 * fragment.
69569800Stomsoft				 */
696232548Strasz				k = 0;
697232548Strasz				while (isset(cg_blksfree(&acg), i) &&
698232548Strasz				    (i >= rounddown(osblock.fs_size - cbase,
69969800Stomsoft				    sblock.fs_frag))) {
70069800Stomsoft					i--;
70169800Stomsoft					k++;
70269800Stomsoft				}
703232548Strasz				if (k)
70469800Stomsoft					acg.cg_frsum[k]--;
705232548Strasz				acg.cg_frsum[k + j]++;
70669800Stomsoft			}
70769800Stomsoft		} else {
70869800Stomsoft			/*
70969800Stomsoft			 * We only grow by some fragments within this last
71069800Stomsoft			 * block.
71169800Stomsoft			 */
712232548Strasz			for (i = sblock.fs_size - cbase - 1;
713232548Strasz			    i >= osblock.fs_size - cbase; i--) {
71469800Stomsoft				setbit(cg_blksfree(&acg), i);
71569800Stomsoft				acg.cg_cs.cs_nffree++;
71669800Stomsoft				j++;
71769800Stomsoft			}
71869800Stomsoft			/*
71969800Stomsoft			 * Lets rejoin a possible partially growed fragment.
72069800Stomsoft			 */
721232548Strasz			k = 0;
722232548Strasz			while (isset(cg_blksfree(&acg), i) &&
723232548Strasz			    (i >= rounddown(osblock.fs_size - cbase,
72469800Stomsoft			    sblock.fs_frag))) {
72569800Stomsoft				i--;
72669800Stomsoft				k++;
72769800Stomsoft			}
728232548Strasz			if (k)
72969800Stomsoft				acg.cg_frsum[k]--;
730232548Strasz			acg.cg_frsum[k + j]++;
73169800Stomsoft		}
73269800Stomsoft	}
73369800Stomsoft
73469800Stomsoft	/*
73569800Stomsoft	 * Handle all new complete blocks here.
73669800Stomsoft	 */
737232548Strasz	for (i = roundup(osblock.fs_size - cbase, sblock.fs_frag);
738232548Strasz	    i + sblock.fs_frag <= dmax - cbase;	/* XXX <= or only < ? */
739232548Strasz	    i += sblock.fs_frag) {
74069800Stomsoft		j = i / sblock.fs_frag;
74169800Stomsoft		setblock(&sblock, cg_blksfree(&acg), j);
74269800Stomsoft		updclst(j);
74369800Stomsoft		acg.cg_cs.cs_nbfree++;
74469800Stomsoft	}
74569800Stomsoft
74669800Stomsoft	/*
74769800Stomsoft	 * Handle the last new block if there are stll some new fragments left.
748114067Sschweikh	 * Here we don't have to bother about the cluster summary or the even
74969800Stomsoft	 * the rotational layout table.
75069800Stomsoft	 */
75169800Stomsoft	if (i < (dmax - cbase)) {
75269800Stomsoft		acg.cg_frsum[dmax - cbase - i]++;
75369800Stomsoft		for (; i < dmax - cbase; i++) {
75469800Stomsoft			setbit(cg_blksfree(&acg), i);
75569800Stomsoft			acg.cg_cs.cs_nffree++;
75669800Stomsoft		}
75769800Stomsoft	}
75869800Stomsoft
75969800Stomsoft	sblock.fs_cstotal.cs_nffree +=
76069800Stomsoft	    (acg.cg_cs.cs_nffree - aocg.cg_cs.cs_nffree);
76169800Stomsoft	sblock.fs_cstotal.cs_nbfree +=
76269800Stomsoft	    (acg.cg_cs.cs_nbfree - aocg.cg_cs.cs_nbfree);
76369800Stomsoft	/*
76469800Stomsoft	 * The following statistics are not changed here:
76569800Stomsoft	 *     sblock.fs_cstotal.cs_ndir
76669800Stomsoft	 *     sblock.fs_cstotal.cs_nifree
76769800Stomsoft	 * As the statistics for this cylinder group are ready, copy it to
76869800Stomsoft	 * the summary information array.
76969800Stomsoft	 */
77069800Stomsoft	*cs = acg.cg_cs;
77169800Stomsoft
77269800Stomsoft	/*
77369800Stomsoft	 * Write the updated "joining" cylinder group back to disk.
77469800Stomsoft	 */
77577885Stomsoft	wtfs(fsbtodb(&sblock, cgtod(&sblock, cylno)), (size_t)sblock.fs_cgsize,
77677885Stomsoft	    (void *)&acg, fso, Nflag);
77769800Stomsoft	DBG_PRINT0("jcg written\n");
778232548Strasz	DBG_DUMP_CG(&sblock, "new joining cg", &acg);
77969800Stomsoft
78069800Stomsoft	DBG_LEAVE;
78169800Stomsoft	return;
78269800Stomsoft}
78369800Stomsoft
78469800Stomsoft/*
785114067Sschweikh * Here we update the location of the cylinder summary. We have two possible
786234846Strasz * ways of growing the cylinder summary:
787114067Sschweikh * (1)	We can try to grow the summary in the current location, and relocate
78869800Stomsoft *	possibly used blocks within the current cylinder group.
78969800Stomsoft * (2)	Alternatively we can relocate the whole cylinder summary to the first
790114067Sschweikh *	new completely empty cylinder group. Once the cylinder summary is no
791114067Sschweikh *	longer in the beginning of the first cylinder group you should never
792114067Sschweikh *	use a version of fsck which is not aware of the possibility to have
79369800Stomsoft *	this structure in a non standard place.
794234178Strasz * Option (2) is considered to be less intrusive to the structure of the file-
795234178Strasz * system, so that's the one being used.
79669800Stomsoft */
79769800Stomsoftstatic void
798217726Smarcelupdcsloc(time_t modtime, int fsi, int fso, unsigned int Nflag)
79969800Stomsoft{
80069800Stomsoft	DBG_FUNC("updcsloc")
801232548Strasz	struct csum *cs;
802232548Strasz	int ocscg, ncscg;
803234178Strasz	ufs2_daddr_t d;
804232548Strasz	int lcs = 0;
805232548Strasz	int block;
80669800Stomsoft
80769800Stomsoft	DBG_ENTER;
80869800Stomsoft
809232548Strasz	if (howmany(sblock.fs_cssize, sblock.fs_fsize) ==
81069800Stomsoft	    howmany(osblock.fs_cssize, osblock.fs_fsize)) {
81169800Stomsoft		/*
81269800Stomsoft		 * No new fragment needed.
81369800Stomsoft		 */
81469800Stomsoft		DBG_LEAVE;
81569800Stomsoft		return;
81669800Stomsoft	}
817232548Strasz	ocscg = dtog(&osblock, osblock.fs_csaddr);
818232548Strasz	cs = fscs + ocscg;
81969800Stomsoft
82069800Stomsoft	/*
82169800Stomsoft	 * Read original cylinder group from disk, and make a copy.
82281311Schm	 * XXX	If Nflag is set in some very rare cases we now miss
82381311Schm	 *	some changes done in updjcg by reading the unmodified
82481311Schm	 *	block from disk.
82569800Stomsoft	 */
82677885Stomsoft	rdfs(fsbtodb(&osblock, cgtod(&osblock, ocscg)),
82777885Stomsoft	    (size_t)osblock.fs_cgsize, (void *)&aocg, fsi);
82869800Stomsoft	DBG_PRINT0("oscg read\n");
829232548Strasz	DBG_DUMP_CG(&sblock, "old summary cg", &aocg);
83069800Stomsoft
83169800Stomsoft	memcpy((void *)&cgun1, (void *)&cgun2, sizeof(cgun2));
83269800Stomsoft
83369800Stomsoft	/*
83469800Stomsoft	 * Touch the cylinder group, set up local variables needed later
83569800Stomsoft	 * and update the superblock.
83669800Stomsoft	 */
837217726Smarcel	acg.cg_time = modtime;
83869800Stomsoft
83969800Stomsoft	/*
84069800Stomsoft	 * XXX	In the case of having active snapshots we may need much more
841114067Sschweikh	 *	blocks for the copy on write. We need each block twice, and
842114067Sschweikh	 *	also up to 8*3 blocks for indirect blocks for all possible
84369800Stomsoft	 *	references.
84469800Stomsoft	 */
845234178Strasz	/*
846234178Strasz	 * There is not enough space in the old cylinder group to
847234178Strasz	 * relocate all blocks as needed, so we relocate the whole
848234178Strasz	 * cylinder group summary to a new group. We try to use the
849234178Strasz	 * first complete new cylinder group just created. Within the
850234178Strasz	 * cylinder group we align the area immediately after the
851234178Strasz	 * cylinder group information location in order to be as
852234178Strasz	 * close as possible to the original implementation of ffs.
853234178Strasz	 *
854234178Strasz	 * First we have to make sure we'll find enough space in the
855234178Strasz	 * new cylinder group. If not, then we currently give up.
856234178Strasz	 * We start with freeing everything which was used by the
857234178Strasz	 * fragments of the old cylinder summary in the current group.
858234178Strasz	 * Now we write back the group meta data, read in the needed
859234178Strasz	 * meta data from the new cylinder group, and start allocating
860234178Strasz	 * within that group. Here we can assume, the group to be
861234178Strasz	 * completely empty. Which makes the handling of fragments and
862234178Strasz	 * clusters a lot easier.
863234178Strasz	 */
864234178Strasz	DBG_TRC;
865234178Strasz	if (sblock.fs_ncg - osblock.fs_ncg < 2)
866234178Strasz		errx(2, "panic: not enough space");
86769800Stomsoft
868234178Strasz	/*
869234178Strasz	 * Point "d" to the first fragment not used by the cylinder
870234178Strasz	 * summary.
871234178Strasz	 */
872234178Strasz	d = osblock.fs_csaddr + (osblock.fs_cssize / osblock.fs_fsize);
87369800Stomsoft
874234178Strasz	/*
875234178Strasz	 * Set up last cluster size ("lcs") already here. Calculate
876234178Strasz	 * the size for the trailing cluster just behind where "d"
877234178Strasz	 * points to.
878234178Strasz	 */
879234178Strasz	if (sblock.fs_contigsumsize > 0) {
880234178Strasz		for (block = howmany(d % sblock.fs_fpg, sblock.fs_frag),
881234189Strasz		    lcs = 0; lcs < sblock.fs_contigsumsize; block++, lcs++) {
882234178Strasz			if (isclr(cg_clustersfree(&acg), block))
883234178Strasz				break;
884234178Strasz		}
885234178Strasz	}
886234178Strasz
887234178Strasz	/*
888234178Strasz	 * Point "d" to the last frag used by the cylinder summary.
889234178Strasz	 */
890234178Strasz	d--;
891234178Strasz
892234178Strasz	DBG_PRINT1("d=%jd\n", (intmax_t)d);
893234178Strasz	if ((d + 1) % sblock.fs_frag) {
89469800Stomsoft		/*
895234178Strasz		 * The end of the cylinder summary is not a complete
896234178Strasz		 * block.
89769800Stomsoft		 */
898234178Strasz		DBG_TRC;
899234178Strasz		frag_adjust(d % sblock.fs_fpg, -1);
900234178Strasz		for (; (d + 1) % sblock.fs_frag; d--) {
901234178Strasz			DBG_PRINT1("d=%jd\n", (intmax_t)d);
902234178Strasz			setbit(cg_blksfree(&acg), d % sblock.fs_fpg);
903234178Strasz			acg.cg_cs.cs_nffree++;
904234178Strasz			sblock.fs_cstotal.cs_nffree++;
90569800Stomsoft		}
90669800Stomsoft		/*
907234178Strasz		 * Point "d" to the last fragment of the last
908234178Strasz		 * (incomplete) block of the cylinder summary.
90969800Stomsoft		 */
910234178Strasz		d++;
911234189Strasz		frag_adjust(d % sblock.fs_fpg, 1);
91269800Stomsoft
913234178Strasz		if (isblock(&sblock, cg_blksfree(&acg),
914234178Strasz		    (d % sblock.fs_fpg) / sblock.fs_frag)) {
915127798Sle			DBG_PRINT1("d=%jd\n", (intmax_t)d);
916234178Strasz			acg.cg_cs.cs_nffree -= sblock.fs_frag;
91769800Stomsoft			acg.cg_cs.cs_nbfree++;
918234178Strasz			sblock.fs_cstotal.cs_nffree -= sblock.fs_frag;
91969800Stomsoft			sblock.fs_cstotal.cs_nbfree++;
920232548Strasz			if (sblock.fs_contigsumsize > 0) {
92169800Stomsoft				setbit(cg_clustersfree(&acg),
922234189Strasz				    (d % sblock.fs_fpg) / sblock.fs_frag);
923232548Strasz				if (lcs < sblock.fs_contigsumsize) {
924232548Strasz					if (lcs)
92569800Stomsoft						cg_clustersum(&acg)[lcs]--;
92669800Stomsoft					lcs++;
92769800Stomsoft					cg_clustersum(&acg)[lcs]++;
92869800Stomsoft				}
92969800Stomsoft			}
93069800Stomsoft		}
93169800Stomsoft		/*
932234178Strasz		 * Point "d" to the first fragment of the block before
933234178Strasz		 * the last incomplete block.
93469800Stomsoft		 */
935234178Strasz		d--;
936234178Strasz	}
93769800Stomsoft
938234178Strasz	DBG_PRINT1("d=%jd\n", (intmax_t)d);
939234178Strasz	for (d = rounddown(d, sblock.fs_frag); d >= osblock.fs_csaddr;
940234178Strasz	    d -= sblock.fs_frag) {
941234178Strasz		DBG_TRC;
942234178Strasz		DBG_PRINT1("d=%jd\n", (intmax_t)d);
943234178Strasz		setblock(&sblock, cg_blksfree(&acg),
944234178Strasz		    (d % sblock.fs_fpg) / sblock.fs_frag);
945234178Strasz		acg.cg_cs.cs_nbfree++;
946234178Strasz		sblock.fs_cstotal.cs_nbfree++;
947234178Strasz		if (sblock.fs_contigsumsize > 0) {
948234178Strasz			setbit(cg_clustersfree(&acg),
949232548Strasz			    (d % sblock.fs_fpg) / sblock.fs_frag);
950234178Strasz			/*
951234178Strasz			 * The last cluster size is already set up.
952234178Strasz			 */
953234178Strasz			if (lcs < sblock.fs_contigsumsize) {
954234178Strasz				if (lcs)
955234178Strasz					cg_clustersum(&acg)[lcs]--;
956234178Strasz				lcs++;
957234178Strasz				cg_clustersum(&acg)[lcs]++;
95869800Stomsoft			}
95969800Stomsoft		}
960234178Strasz	}
961234178Strasz	*cs = acg.cg_cs;
96269800Stomsoft
96369800Stomsoft	/*
964234178Strasz	 * Now write the former cylinder group containing the cylinder
965234178Strasz	 * summary back to disk.
96669800Stomsoft	 */
967234178Strasz	wtfs(fsbtodb(&sblock, cgtod(&sblock, ocscg)),
968234178Strasz	    (size_t)sblock.fs_cgsize, (void *)&acg, fso, Nflag);
969234178Strasz	DBG_PRINT0("oscg written\n");
970234178Strasz	DBG_DUMP_CG(&sblock, "old summary cg", &acg);
97169800Stomsoft
97269800Stomsoft	/*
973234178Strasz	 * Find the beginning of the new cylinder group containing the
974234178Strasz	 * cylinder summary.
97569800Stomsoft	 */
976234178Strasz	sblock.fs_csaddr = cgdmin(&sblock, osblock.fs_ncg);
977234178Strasz	ncscg = dtog(&sblock, sblock.fs_csaddr);
978234178Strasz	cs = fscs + ncscg;
97969800Stomsoft
98069800Stomsoft	/*
981234178Strasz	 * If Nflag is specified, we would now read random data instead
982234178Strasz	 * of an empty cg structure from disk. So we can't simulate that
983234178Strasz	 * part for now.
98469800Stomsoft	 */
985234178Strasz	if (Nflag) {
986234178Strasz		DBG_PRINT0("nscg update skipped\n");
987234178Strasz		DBG_LEAVE;
988234178Strasz		return;
98969800Stomsoft	}
99069800Stomsoft
99169800Stomsoft	/*
992234178Strasz	 * Read the future cylinder group containing the cylinder
993234178Strasz	 * summary from disk, and make a copy.
99469800Stomsoft	 */
995234178Strasz	rdfs(fsbtodb(&sblock, cgtod(&sblock, ncscg)),
996234178Strasz	    (size_t)sblock.fs_cgsize, (void *)&aocg, fsi);
997234178Strasz	DBG_PRINT0("nscg read\n");
998234178Strasz	DBG_DUMP_CG(&sblock, "new summary cg", &aocg);
99969800Stomsoft
1000234178Strasz	memcpy((void *)&cgun1, (void *)&cgun2, sizeof(cgun2));
1001234178Strasz
100269800Stomsoft	/*
1003234178Strasz	 * Allocate all complete blocks used by the new cylinder
1004234178Strasz	 * summary.
100569800Stomsoft	 */
1006234178Strasz	for (d = sblock.fs_csaddr; d + sblock.fs_frag <=
1007234178Strasz	    sblock.fs_csaddr + (sblock.fs_cssize / sblock.fs_fsize);
1008234178Strasz	    d += sblock.fs_frag) {
1009234178Strasz		clrblock(&sblock, cg_blksfree(&acg),
1010234178Strasz		    (d % sblock.fs_fpg) / sblock.fs_frag);
1011234178Strasz		acg.cg_cs.cs_nbfree--;
1012234178Strasz		sblock.fs_cstotal.cs_nbfree--;
1013234178Strasz		if (sblock.fs_contigsumsize > 0) {
1014234178Strasz			clrbit(cg_clustersfree(&acg),
1015234178Strasz			    (d % sblock.fs_fpg) / sblock.fs_frag);
101669800Stomsoft		}
101769800Stomsoft	}
101869800Stomsoft
101969800Stomsoft	/*
1020234178Strasz	 * Allocate all fragments used by the cylinder summary in the
1021234178Strasz	 * last block.
102269800Stomsoft	 */
1023234189Strasz	if (d < sblock.fs_csaddr + (sblock.fs_cssize / sblock.fs_fsize)) {
1024234178Strasz		for (; d - sblock.fs_csaddr <
1025234178Strasz		    sblock.fs_cssize/sblock.fs_fsize; d++) {
1026234178Strasz			clrbit(cg_blksfree(&acg), d % sblock.fs_fpg);
1027234178Strasz			acg.cg_cs.cs_nffree--;
1028234178Strasz			sblock.fs_cstotal.cs_nffree--;
102969800Stomsoft		}
1030234178Strasz		acg.cg_cs.cs_nbfree--;
1031234178Strasz		acg.cg_cs.cs_nffree += sblock.fs_frag;
1032234178Strasz		sblock.fs_cstotal.cs_nbfree--;
1033234178Strasz		sblock.fs_cstotal.cs_nffree += sblock.fs_frag;
1034234178Strasz		if (sblock.fs_contigsumsize > 0)
1035234178Strasz			clrbit(cg_clustersfree(&acg),
1036234178Strasz			    (d % sblock.fs_fpg) / sblock.fs_frag);
103769800Stomsoft
1038234178Strasz		frag_adjust(d % sblock.fs_fpg, 1);
103969800Stomsoft	}
104069800Stomsoft	/*
1041234178Strasz	 * XXX	Handle the cluster statistics here in the case this
1042234178Strasz	 *	cylinder group is now almost full, and the remaining
1043234178Strasz	 *	space is less then the maximum cluster size. This is
1044234178Strasz	 *	probably not needed, as you would hardly find a file
1045234178Strasz	 *	system which has only MAXCSBUFS+FS_MAXCONTIG of free
1046234178Strasz	 *	space right behind the cylinder group information in
1047234178Strasz	 *	any new cylinder group.
104869800Stomsoft	 */
104969800Stomsoft
1050234178Strasz	/*
1051234178Strasz	 * Update our statistics in the cylinder summary.
1052234178Strasz	 */
105369800Stomsoft	*cs = acg.cg_cs;
105469800Stomsoft
105569800Stomsoft	/*
1056234178Strasz	 * Write the new cylinder group containing the cylinder summary
1057234178Strasz	 * back to disk.
105869800Stomsoft	 */
1059234178Strasz	wtfs(fsbtodb(&sblock, cgtod(&sblock, ncscg)),
1060234178Strasz	    (size_t)sblock.fs_cgsize, (void *)&acg, fso, Nflag);
1061234178Strasz	DBG_PRINT0("nscg written\n");
1062232548Strasz	DBG_DUMP_CG(&sblock, "new summary cg", &acg);
106369800Stomsoft
106469800Stomsoft	DBG_LEAVE;
106569800Stomsoft	return;
106669800Stomsoft}
106769800Stomsoft
106869800Stomsoft/*
106969800Stomsoft * Here we read some block(s) from disk.
107069800Stomsoft */
107169800Stomsoftstatic void
107298542Smckusickrdfs(ufs2_daddr_t bno, size_t size, void *bf, int fsi)
107369800Stomsoft{
107469800Stomsoft	DBG_FUNC("rdfs")
107577885Stomsoft	ssize_t	n;
107669800Stomsoft
107769800Stomsoft	DBG_ENTER;
107869800Stomsoft
1079232548Strasz	if (bno < 0)
1080140351Scharnier		err(32, "rdfs: attempting to read negative block number");
1081232548Strasz	if (lseek(fsi, (off_t)bno * DEV_BSIZE, 0) < 0)
1082127798Sle		err(33, "rdfs: seek error: %jd", (intmax_t)bno);
108377885Stomsoft	n = read(fsi, bf, size);
1084232548Strasz	if (n != (ssize_t)size)
1085127798Sle		err(34, "rdfs: read error: %jd", (intmax_t)bno);
108669800Stomsoft
108769800Stomsoft	DBG_LEAVE;
108869800Stomsoft	return;
108969800Stomsoft}
109069800Stomsoft
109169800Stomsoft/*
109269800Stomsoft * Here we write some block(s) to disk.
109369800Stomsoft */
109469800Stomsoftstatic void
109598542Smckusickwtfs(ufs2_daddr_t bno, size_t size, void *bf, int fso, unsigned int Nflag)
109669800Stomsoft{
109769800Stomsoft	DBG_FUNC("wtfs")
109877885Stomsoft	ssize_t	n;
109969800Stomsoft
110069800Stomsoft	DBG_ENTER;
110169800Stomsoft
110269800Stomsoft	if (Nflag) {
110369800Stomsoft		DBG_LEAVE;
110469800Stomsoft		return;
110569800Stomsoft	}
1106232548Strasz	if (lseek(fso, (off_t)bno * DEV_BSIZE, SEEK_SET) < 0)
110769926Stomsoft		err(35, "wtfs: seek error: %ld", (long)bno);
110877885Stomsoft	n = write(fso, bf, size);
1109232548Strasz	if (n != (ssize_t)size)
111069926Stomsoft		err(36, "wtfs: write error: %ld", (long)bno);
111169800Stomsoft
111269800Stomsoft	DBG_LEAVE;
111369800Stomsoft	return;
111469800Stomsoft}
111569800Stomsoft
111669800Stomsoft/*
1117114067Sschweikh * Here we check if all frags of a block are free. For more details again
111869800Stomsoft * please see the source of newfs(8), as this function is taken over almost
111969800Stomsoft * unchanged.
112069800Stomsoft */
112169800Stomsoftstatic int
112269800Stomsoftisblock(struct fs *fs, unsigned char *cp, int h)
112369800Stomsoft{
112469800Stomsoft	DBG_FUNC("isblock")
1125232548Strasz	unsigned char mask;
112669800Stomsoft
112769800Stomsoft	DBG_ENTER;
112869800Stomsoft
112969800Stomsoft	switch (fs->fs_frag) {
113069800Stomsoft	case 8:
113169800Stomsoft		DBG_LEAVE;
113269800Stomsoft		return (cp[h] == 0xff);
113369800Stomsoft	case 4:
113469800Stomsoft		mask = 0x0f << ((h & 0x1) << 2);
113569800Stomsoft		DBG_LEAVE;
113669800Stomsoft		return ((cp[h >> 1] & mask) == mask);
113769800Stomsoft	case 2:
113869800Stomsoft		mask = 0x03 << ((h & 0x3) << 1);
113969800Stomsoft		DBG_LEAVE;
114069800Stomsoft		return ((cp[h >> 2] & mask) == mask);
114169800Stomsoft	case 1:
114269800Stomsoft		mask = 0x01 << (h & 0x7);
114369800Stomsoft		DBG_LEAVE;
114469800Stomsoft		return ((cp[h >> 3] & mask) == mask);
114569800Stomsoft	default:
114669800Stomsoft		fprintf(stderr, "isblock bad fs_frag %d\n", fs->fs_frag);
114769800Stomsoft		DBG_LEAVE;
114869800Stomsoft		return (0);
114969800Stomsoft	}
115069800Stomsoft}
115169800Stomsoft
115269800Stomsoft/*
115369800Stomsoft * Here we allocate a complete block in the block map. For more details again
1154114067Sschweikh * please see the source of newfs(8), as this function is taken over almost
115569800Stomsoft * unchanged.
115669800Stomsoft */
115769800Stomsoftstatic void
115869800Stomsoftclrblock(struct fs *fs, unsigned char *cp, int h)
115969800Stomsoft{
116069800Stomsoft	DBG_FUNC("clrblock")
116169800Stomsoft
116269800Stomsoft	DBG_ENTER;
116369800Stomsoft
116469800Stomsoft	switch ((fs)->fs_frag) {
116569800Stomsoft	case 8:
116669800Stomsoft		cp[h] = 0;
116769800Stomsoft		break;
116869800Stomsoft	case 4:
116969800Stomsoft		cp[h >> 1] &= ~(0x0f << ((h & 0x1) << 2));
117069800Stomsoft		break;
117169800Stomsoft	case 2:
117269800Stomsoft		cp[h >> 2] &= ~(0x03 << ((h & 0x3) << 1));
117369800Stomsoft		break;
117469800Stomsoft	case 1:
117569800Stomsoft		cp[h >> 3] &= ~(0x01 << (h & 0x7));
117669800Stomsoft		break;
117769800Stomsoft	default:
117869926Stomsoft		warnx("clrblock bad fs_frag %d", fs->fs_frag);
117969800Stomsoft		break;
118069800Stomsoft	}
118169800Stomsoft
118269800Stomsoft	DBG_LEAVE;
118369800Stomsoft	return;
118469800Stomsoft}
118569800Stomsoft
118669800Stomsoft/*
118769800Stomsoft * Here we free a complete block in the free block map. For more details again
1188114067Sschweikh * please see the source of newfs(8), as this function is taken over almost
118969800Stomsoft * unchanged.
119069800Stomsoft */
119169800Stomsoftstatic void
119269800Stomsoftsetblock(struct fs *fs, unsigned char *cp, int h)
119369800Stomsoft{
119469800Stomsoft	DBG_FUNC("setblock")
119569800Stomsoft
119669800Stomsoft	DBG_ENTER;
119769800Stomsoft
119869800Stomsoft	switch (fs->fs_frag) {
119969800Stomsoft	case 8:
120069800Stomsoft		cp[h] = 0xff;
120169800Stomsoft		break;
120269800Stomsoft	case 4:
120369800Stomsoft		cp[h >> 1] |= (0x0f << ((h & 0x1) << 2));
120469800Stomsoft		break;
120569800Stomsoft	case 2:
120669800Stomsoft		cp[h >> 2] |= (0x03 << ((h & 0x3) << 1));
120769800Stomsoft		break;
120869800Stomsoft	case 1:
120969800Stomsoft		cp[h >> 3] |= (0x01 << (h & 0x7));
121069800Stomsoft		break;
121169800Stomsoft	default:
121269926Stomsoft		warnx("setblock bad fs_frag %d", fs->fs_frag);
121369800Stomsoft		break;
121469800Stomsoft	}
121569800Stomsoft
121669800Stomsoft	DBG_LEAVE;
121769800Stomsoft	return;
121869800Stomsoft}
121969800Stomsoft
122069800Stomsoft/*
122169800Stomsoft * Figure out how many lines our current terminal has. For more details again
1222114067Sschweikh * please see the source of newfs(8), as this function is taken over almost
122369800Stomsoft * unchanged.
122469800Stomsoft */
122569800Stomsoftstatic int
122669800Stomsoftcharsperline(void)
122769800Stomsoft{
122869800Stomsoft	DBG_FUNC("charsperline")
1229232548Strasz	int columns;
1230232548Strasz	char *cp;
1231232548Strasz	struct winsize ws;
123269800Stomsoft
123369800Stomsoft	DBG_ENTER;
123469800Stomsoft
123569800Stomsoft	columns = 0;
1236232548Strasz	if (ioctl(0, TIOCGWINSZ, &ws) != -1)
123769800Stomsoft		columns = ws.ws_col;
1238232548Strasz	if (columns == 0 && (cp = getenv("COLUMNS")))
123969800Stomsoft		columns = atoi(cp);
1240232548Strasz	if (columns == 0)
124169800Stomsoft		columns = 80;	/* last resort */
124269800Stomsoft
124369800Stomsoft	DBG_LEAVE;
1244234420Strasz	return (columns);
124569800Stomsoft}
124669800Stomsoft
1247234846Straszstatic int
1248234846Straszis_dev(const char *name)
1249234846Strasz{
1250234846Strasz	struct stat devstat;
1251234846Strasz
1252234846Strasz	if (stat(name, &devstat) != 0)
1253234846Strasz		return (0);
1254234846Strasz	if (!S_ISCHR(devstat.st_mode))
1255234846Strasz		return (0);
1256234846Strasz	return (1);
1257234846Strasz}
1258234846Strasz
1259114936Sgrog/*
1260234846Strasz * Return mountpoint on which the device is currently mounted.
1261234846Strasz */
1262234846Straszstatic const struct statfs *
1263234846Straszdev_to_statfs(const char *dev)
1264114936Sgrog{
1265234846Strasz	struct stat devstat, mntdevstat;
1266234846Strasz	struct statfs *mntbuf, *statfsp;
1267234846Strasz	char device[MAXPATHLEN];
1268234846Strasz	char *mntdevname;
1269234846Strasz	int i, mntsize;
1270114936Sgrog
1271234846Strasz	/*
1272234846Strasz	 * First check the mounted filesystems.
1273234846Strasz	 */
1274234846Strasz	if (stat(dev, &devstat) != 0)
1275234846Strasz		return (NULL);
1276234846Strasz	if (!S_ISCHR(devstat.st_mode) && !S_ISBLK(devstat.st_mode))
1277234846Strasz		return (NULL);
1278114936Sgrog
1279234846Strasz	mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
1280234846Strasz	for (i = 0; i < mntsize; i++) {
1281234846Strasz		statfsp = &mntbuf[i];
1282234846Strasz		mntdevname = statfsp->f_mntfromname;
1283234846Strasz		if (*mntdevname != '/') {
1284234846Strasz			strcpy(device, _PATH_DEV);
1285234846Strasz			strcat(device, mntdevname);
1286234846Strasz			mntdevname = device;
1287234846Strasz		}
1288234846Strasz		if (stat(mntdevname, &mntdevstat) == 0 &&
1289234846Strasz		    mntdevstat.st_rdev == devstat.st_rdev)
1290234846Strasz			return (statfsp);
1291234846Strasz	}
1292114936Sgrog
1293234846Strasz	return (NULL);
1294114936Sgrog}
1295114936Sgrog
1296234846Straszstatic const char *
1297234846Straszmountpoint_to_dev(const char *mountpoint)
1298234846Strasz{
1299234846Strasz	struct statfs *mntbuf, *statfsp;
1300234846Strasz	struct fstab *fs;
1301234846Strasz	int i, mntsize;
1302234846Strasz
1303234846Strasz	/*
1304234846Strasz	 * First check the mounted filesystems.
1305234846Strasz	 */
1306234846Strasz	mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
1307234846Strasz	for (i = 0; i < mntsize; i++) {
1308234846Strasz		statfsp = &mntbuf[i];
1309234846Strasz
1310234846Strasz		if (strcmp(statfsp->f_mntonname, mountpoint) == 0)
1311234846Strasz			return (statfsp->f_mntfromname);
1312234846Strasz	}
1313234846Strasz
1314234846Strasz	/*
1315234846Strasz	 * Check the fstab.
1316234846Strasz	 */
1317234846Strasz	fs = getfsfile(mountpoint);
1318234846Strasz	if (fs != NULL)
1319234846Strasz		return (fs->fs_spec);
1320234846Strasz
1321234846Strasz	return (NULL);
1322234846Strasz}
1323234846Strasz
1324234846Straszstatic const char *
1325234846Straszgetdev(const char *name)
1326234846Strasz{
1327234846Strasz	static char device[MAXPATHLEN];
1328234846Strasz	const char *cp, *dev;
1329234846Strasz
1330234846Strasz	if (is_dev(name))
1331234846Strasz		return (name);
1332234846Strasz
1333234846Strasz	cp = strrchr(name, '/');
1334234846Strasz	if (cp == 0) {
1335234846Strasz		snprintf(device, sizeof(device), "%s%s", _PATH_DEV, name);
1336234846Strasz		if (is_dev(device))
1337234846Strasz			return (device);
1338234846Strasz	}
1339234846Strasz
1340234846Strasz	dev = mountpoint_to_dev(name);
1341234846Strasz	if (dev != NULL && is_dev(dev))
1342234846Strasz		return (dev);
1343234846Strasz
1344234846Strasz	return (NULL);
1345234846Strasz}
1346234846Strasz
134769800Stomsoft/*
1348232548Strasz * growfs(8) is a utility which allows to increase the size of an existing
1349223652Strasz * ufs file system. Currently this can only be done on unmounted file system.
1350114067Sschweikh * It recognizes some command line options to specify the new desired size,
1351223652Strasz * and it does some basic checkings. The old file system size is determined
1352114067Sschweikh * and after some more checks like we can really access the new last block
135369800Stomsoft * on the disk etc. we calculate the new parameters for the superblock. After
1354233656Strasz * having done this we just call growfs() which will do the work.
135569800Stomsoft * We still have to provide support for snapshots. Therefore we first have to
1356114067Sschweikh * understand what data structures are always replicated in the snapshot on
1357114067Sschweikh * creation, for all other blocks we touch during our procedure, we have to
135869926Stomsoft * keep the old blocks unchanged somewhere available for the snapshots. If we
1359114067Sschweikh * are lucky, then we only have to handle our blocks to be relocated in that
136069800Stomsoft * way.
1361114067Sschweikh * Also we have to consider in what order we actually update the critical
1362223652Strasz * data structures of the file system to make sure, that in case of a disaster
136369800Stomsoft * fsck(8) is still able to restore any lost data.
1364114067Sschweikh * The foreseen last step then will be to provide for growing even mounted
1365223652Strasz * file systems. There we have to extend the mount() system call to provide
1366223652Strasz * userland access to the file system locking facility.
136769800Stomsoft */
136869800Stomsoftint
136969800Stomsoftmain(int argc, char **argv)
137069800Stomsoft{
137169800Stomsoft	DBG_FUNC("main")
1372234846Strasz	const char *device;
1373234846Strasz	const struct statfs *statfsp;
1374234846Strasz	uint64_t size = 0;
1375234846Strasz	off_t mediasize;
1376234846Strasz	int error, i, j, fsi, fso, ch, Nflag = 0, yflag = 0;
1377234846Strasz	char *p, reply[5], oldsizebuf[6], newsizebuf[6];
1378234846Strasz	void *testbuf;
137969800Stomsoft
138069800Stomsoft	DBG_ENTER;
138169800Stomsoft
1382232548Strasz	while ((ch = getopt(argc, argv, "Ns:vy")) != -1) {
138369800Stomsoft		switch(ch) {
138469800Stomsoft		case 'N':
1385232548Strasz			Nflag = 1;
138669800Stomsoft			break;
138769800Stomsoft		case 's':
1388234846Strasz			size = (off_t)strtoumax(optarg, &p, 0);
1389234846Strasz			if (p == NULL || *p == '\0')
1390234846Strasz				size *= DEV_BSIZE;
1391234846Strasz			else if (*p == 'b' || *p == 'B')
1392234846Strasz				; /* do nothing */
1393234846Strasz			else if (*p == 'k' || *p == 'K')
1394234846Strasz				size <<= 10;
1395234846Strasz			else if (*p == 'm' || *p == 'M')
1396234846Strasz				size <<= 20;
1397234846Strasz			else if (*p == 'g' || *p == 'G')
1398234846Strasz				size <<= 30;
1399234846Strasz			else if (*p == 't' || *p == 'T') {
1400234846Strasz				size <<= 30;
1401234846Strasz				size <<= 10;
1402234846Strasz			} else
1403234846Strasz				errx(1, "unknown suffix on -s argument");
140469800Stomsoft			break;
140569800Stomsoft		case 'v': /* for compatibility to newfs */
140669800Stomsoft			break;
140769800Stomsoft		case 'y':
1408234846Strasz			yflag = 1;
140969800Stomsoft			break;
141069800Stomsoft		case '?':
141169800Stomsoft			/* FALLTHROUGH */
141269800Stomsoft		default:
141369926Stomsoft			usage();
141469800Stomsoft		}
141569800Stomsoft	}
141669800Stomsoft	argc -= optind;
141769800Stomsoft	argv += optind;
141869800Stomsoft
1419232548Strasz	if (argc != 1)
142069926Stomsoft		usage();
142169800Stomsoft
142269800Stomsoft	/*
1423234846Strasz	 * Now try to guess the device name.
142469800Stomsoft	 */
1425234846Strasz	device = getdev(*argv);
1426234846Strasz	if (device == NULL)
1427234846Strasz		errx(1, "cannot find special device for %s", *argv);
142869800Stomsoft
1429234846Strasz	statfsp = dev_to_statfs(device);
143069800Stomsoft
143169800Stomsoft	fsi = open(device, O_RDONLY);
1432232548Strasz	if (fsi < 0)
143369926Stomsoft		err(1, "%s", device);
143469800Stomsoft
143569800Stomsoft	/*
1436234846Strasz	 * Try to guess the slice size if not specified.
143769800Stomsoft	 */
1438234846Strasz	if (ioctl(fsi, DIOCGMEDIASIZE, &mediasize) == -1)
1439234846Strasz		err(1,"DIOCGMEDIASIZE");
144069800Stomsoft
144169800Stomsoft	/*
1442223652Strasz	 * Check if that partition is suitable for growing a file system.
144369800Stomsoft	 */
1444234846Strasz	if (mediasize < 1)
144569926Stomsoft		errx(1, "partition is unavailable");
144669800Stomsoft
144769800Stomsoft	/*
144869800Stomsoft	 * Read the current superblock, and take a backup.
144969800Stomsoft	 */
145098542Smckusick	for (i = 0; sblock_try[i] != -1; i++) {
145198542Smckusick		sblockloc = sblock_try[i] / DEV_BSIZE;
145298542Smckusick		rdfs(sblockloc, (size_t)SBLOCKSIZE, (void *)&(osblock), fsi);
145398542Smckusick		if ((osblock.fs_magic == FS_UFS1_MAGIC ||
1454232548Strasz		    (osblock.fs_magic == FS_UFS2_MAGIC &&
1455232548Strasz		    osblock.fs_sblockloc == sblock_try[i])) &&
145698542Smckusick		    osblock.fs_bsize <= MAXBSIZE &&
1457127798Sle		    osblock.fs_bsize >= (int32_t) sizeof(struct fs))
145898542Smckusick			break;
145998542Smckusick	}
1460232548Strasz	if (sblock_try[i] == -1)
146169926Stomsoft		errx(1, "superblock not recognized");
146269800Stomsoft	memcpy((void *)&fsun1, (void *)&fsun2, sizeof(fsun2));
146369800Stomsoft
146469800Stomsoft	DBG_OPEN("/tmp/growfs.debug"); /* already here we need a superblock */
1465232548Strasz	DBG_DUMP_FS(&sblock, "old sblock");
146669800Stomsoft
146769800Stomsoft	/*
1468233656Strasz	 * Determine size to grow to. Default to the device size.
146969800Stomsoft	 */
1470234846Strasz	if (size == 0)
1471234846Strasz		size = mediasize;
1472234846Strasz	else {
1473234846Strasz		if (size > (uint64_t)mediasize) {
1474234846Strasz			humanize_number(oldsizebuf, sizeof(oldsizebuf), size,
1475234846Strasz			    "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
1476234846Strasz			humanize_number(newsizebuf, sizeof(newsizebuf),
1477234846Strasz			    mediasize,
1478234846Strasz			    "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
1479234846Strasz
1480234846Strasz			errx(1, "requested size %s is larger "
1481234846Strasz			    "than the available %s", oldsizebuf, newsizebuf);
1482234846Strasz		}
148369800Stomsoft	}
148469800Stomsoft
1485244243Strasz	/*
1486244243Strasz	 * Make sure the new size is a multiple of fs_fsize; /dev/ufssuspend
1487244243Strasz	 * only supports fragment-aligned IO requests.
1488244243Strasz	 */
1489244243Strasz	size -= size % osblock.fs_fsize;
1490244243Strasz
1491234846Strasz	if (size <= (uint64_t)(osblock.fs_size * osblock.fs_fsize)) {
1492234846Strasz		humanize_number(oldsizebuf, sizeof(oldsizebuf),
1493234846Strasz		    osblock.fs_size * osblock.fs_fsize,
1494234846Strasz		    "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
1495234846Strasz		humanize_number(newsizebuf, sizeof(newsizebuf), size,
1496234846Strasz		    "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
1497234846Strasz
1498234846Strasz		errx(1, "requested size %s is not larger than the current "
1499234846Strasz		   "filesystem size %s", newsizebuf, oldsizebuf);
1500234846Strasz	}
1501234846Strasz
1502234846Strasz	sblock.fs_size = dbtofsb(&osblock, size / DEV_BSIZE);
1503242379Strasz	sblock.fs_providersize = dbtofsb(&osblock, mediasize / DEV_BSIZE);
1504234846Strasz
150569800Stomsoft	/*
1506234846Strasz	 * Are we really growing?
150769800Stomsoft	 */
1508232548Strasz	if (osblock.fs_size >= sblock.fs_size) {
1509127798Sle		errx(1, "we are not growing (%jd->%jd)",
1510127798Sle		    (intmax_t)osblock.fs_size, (intmax_t)sblock.fs_size);
151169800Stomsoft	}
151269800Stomsoft
151369800Stomsoft	/*
151469800Stomsoft	 * Check if we find an active snapshot.
151569800Stomsoft	 */
1516234846Strasz	if (yflag == 0) {
1517232548Strasz		for (j = 0; j < FSMAXSNAP; j++) {
1518232548Strasz			if (sblock.fs_snapinum[j]) {
1519223652Strasz				errx(1, "active snapshot found in file system; "
1520223429Strasz				    "please remove all snapshots before "
1521140351Scharnier				    "using growfs");
152269800Stomsoft			}
1523232548Strasz			if (!sblock.fs_snapinum[j]) /* list is dense */
152469800Stomsoft				break;
152569800Stomsoft		}
152669800Stomsoft	}
152769800Stomsoft
1528234846Strasz	if (yflag == 0 && Nflag == 0) {
1529234846Strasz		if (statfsp != NULL && (statfsp->f_flags & MNT_RDONLY) == 0)
1530243246Strasz			printf("Device is mounted read-write; resizing will "
1531243246Strasz			    "result in temporary write suspension for %s.\n",
1532243246Strasz			    statfsp->f_mntonname);
1533234846Strasz		printf("It's strongly recommended to make a backup "
1534223652Strasz		    "before growing the file system.\n"
1535234846Strasz		    "OK to grow filesystem on %s", device);
1536234846Strasz		if (statfsp != NULL)
1537234846Strasz			printf(", mounted on %s,", statfsp->f_mntonname);
1538234846Strasz		humanize_number(oldsizebuf, sizeof(oldsizebuf),
1539234846Strasz		    osblock.fs_size * osblock.fs_fsize,
1540234846Strasz		    "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
1541234846Strasz		humanize_number(newsizebuf, sizeof(newsizebuf),
1542234846Strasz		    sblock.fs_size * sblock.fs_fsize,
1543234846Strasz		    "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
1544234846Strasz		printf(" from %s to %s? [Yes/No] ", oldsizebuf, newsizebuf);
1545234846Strasz		fflush(stdout);
154677885Stomsoft		fgets(reply, (int)sizeof(reply), stdin);
1547250377Seadler		if (strcasecmp(reply, "Yes\n")){
1548223429Strasz			printf("\nNothing done\n");
154969800Stomsoft			exit (0);
1550114067Sschweikh		}
155169800Stomsoft	}
155269800Stomsoft
1553234846Strasz	/*
1554234846Strasz	 * Try to access our device for writing.  If it's not mounted,
1555234846Strasz	 * or mounted read-only, simply open it; otherwise, use UFS
1556234846Strasz	 * suspension mechanism.
1557234846Strasz	 */
1558234846Strasz	if (Nflag) {
1559234846Strasz		fso = -1;
1560234846Strasz	} else {
1561243246Strasz		if (statfsp != NULL && (statfsp->f_flags & MNT_RDONLY) == 0) {
1562243246Strasz			fso = open(_PATH_UFSSUSPEND, O_RDWR);
1563243246Strasz			if (fso == -1)
1564243246Strasz				err(1, "unable to open %s", _PATH_UFSSUSPEND);
1565243246Strasz			error = ioctl(fso, UFSSUSPEND, &statfsp->f_fsid);
1566243246Strasz			if (error != 0)
1567243246Strasz				err(1, "UFSSUSPEND");
1568243246Strasz		} else {
1569243246Strasz			fso = open(device, O_WRONLY);
1570243246Strasz			if (fso < 0)
1571243246Strasz				err(1, "%s", device);
1572243246Strasz		}
1573234846Strasz	}
157469800Stomsoft
157569800Stomsoft	/*
1576234846Strasz	 * Try to access our new last block in the file system.
157769800Stomsoft	 */
1578234846Strasz	testbuf = malloc(sblock.fs_fsize);
1579234846Strasz	if (testbuf == NULL)
1580234846Strasz		err(1, "malloc");
1581235079Strasz	rdfs((ufs2_daddr_t)((size - sblock.fs_fsize) / DEV_BSIZE),
1582234846Strasz	    sblock.fs_fsize, testbuf, fsi);
1583235079Strasz	wtfs((ufs2_daddr_t)((size - sblock.fs_fsize) / DEV_BSIZE),
1584234846Strasz	    sblock.fs_fsize, testbuf, fso, Nflag);
1585234846Strasz	free(testbuf);
158669800Stomsoft
158769800Stomsoft	/*
158869800Stomsoft	 * Now calculate new superblock values and check for reasonable
1589223652Strasz	 * bound for new file system size:
1590233656Strasz	 *     fs_size:    is derived from user input
159169800Stomsoft	 *     fs_dsize:   should get updated in the routines creating or
159269800Stomsoft	 *                 updating the cylinder groups on the fly
159369800Stomsoft	 *     fs_cstotal: should get updated in the routines creating or
159469800Stomsoft	 *                 updating the cylinder groups
159569800Stomsoft	 */
159669800Stomsoft
159769800Stomsoft	/*
1598223652Strasz	 * Update the number of cylinders and cylinder groups in the file system.
159969800Stomsoft	 */
160098542Smckusick	if (sblock.fs_magic == FS_UFS1_MAGIC) {
160198542Smckusick		sblock.fs_old_ncyl =
160298542Smckusick		    sblock.fs_size * sblock.fs_old_nspf / sblock.fs_old_spc;
160398542Smckusick		if (sblock.fs_size * sblock.fs_old_nspf >
160498542Smckusick		    sblock.fs_old_ncyl * sblock.fs_old_spc)
160598542Smckusick			sblock.fs_old_ncyl++;
160669800Stomsoft	}
160798542Smckusick	sblock.fs_ncg = howmany(sblock.fs_size, sblock.fs_fpg);
160869800Stomsoft
1609244295Strasz	/*
1610244295Strasz	 * Allocate last cylinder group only if there is enough room
1611244295Strasz	 * for at least one data block.
1612244295Strasz	 */
161398542Smckusick	if (sblock.fs_size % sblock.fs_fpg != 0 &&
1614244295Strasz	    sblock.fs_size <= cgdmin(&sblock, sblock.fs_ncg - 1)) {
1615244295Strasz		humanize_number(oldsizebuf, sizeof(oldsizebuf),
1616244295Strasz		    (sblock.fs_size % sblock.fs_fpg) * sblock.fs_fsize,
1617244295Strasz		    "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
1618244295Strasz		warnx("no room to allocate last cylinder group; "
1619244295Strasz		    "leaving %s unused", oldsizebuf);
162069800Stomsoft		sblock.fs_ncg--;
162198542Smckusick		if (sblock.fs_magic == FS_UFS1_MAGIC)
162298542Smckusick			sblock.fs_old_ncyl = sblock.fs_ncg * sblock.fs_old_cpg;
162398542Smckusick		sblock.fs_size = sblock.fs_ncg * sblock.fs_fpg;
162469800Stomsoft	}
162569800Stomsoft
162669800Stomsoft	/*
162769800Stomsoft	 * Update the space for the cylinder group summary information in the
162869800Stomsoft	 * respective cylinder group data area.
162969800Stomsoft	 */
163069800Stomsoft	sblock.fs_cssize =
163169800Stomsoft	    fragroundup(&sblock, sblock.fs_ncg * sizeof(struct csum));
1632114067Sschweikh
1633232548Strasz	if (osblock.fs_size >= sblock.fs_size)
163469926Stomsoft		errx(1, "not enough new space");
163569800Stomsoft
163669800Stomsoft	DBG_PRINT0("sblock calculated\n");
163769800Stomsoft
163869800Stomsoft	/*
163969800Stomsoft	 * Ok, everything prepared, so now let's do the tricks.
164069800Stomsoft	 */
164169800Stomsoft	growfs(fsi, fso, Nflag);
164269800Stomsoft
164369800Stomsoft	close(fsi);
1644234846Strasz	if (fso > -1) {
1645243246Strasz		if (statfsp != NULL && (statfsp->f_flags & MNT_RDONLY) == 0) {
1646243246Strasz			error = ioctl(fso, UFSRESUME);
1647243246Strasz			if (error != 0)
1648243246Strasz				err(1, "UFSRESUME");
1649243246Strasz		}
1650234846Strasz		error = close(fso);
1651234846Strasz		if (error != 0)
1652234846Strasz			err(1, "close");
1653243246Strasz		if (statfsp != NULL && (statfsp->f_flags & MNT_RDONLY) != 0)
1654243246Strasz			mount_reload(statfsp);
1655234846Strasz	}
165669800Stomsoft
165769800Stomsoft	DBG_CLOSE;
165869800Stomsoft
165969800Stomsoft	DBG_LEAVE;
1660234420Strasz	return (0);
166169800Stomsoft}
166269800Stomsoft
166369800Stomsoft/*
166469800Stomsoft * Dump a line of usage.
166569800Stomsoft */
166669800Stomsoftstatic void
166769926Stomsoftusage(void)
1668114067Sschweikh{
166969800Stomsoft	DBG_FUNC("usage")
167069800Stomsoft
167169800Stomsoft	DBG_ENTER;
167269800Stomsoft
1673234846Strasz	fprintf(stderr, "usage: growfs [-Ny] [-s size] special | filesystem\n");
167469926Stomsoft
167569800Stomsoft	DBG_LEAVE;
167669926Stomsoft	exit(1);
167769800Stomsoft}
167869800Stomsoft
167969800Stomsoft/*
1680114067Sschweikh * This updates most parameters and the bitmap related to cluster. We have to
1681114067Sschweikh * assume that sblock, osblock, acg are set up.
168269800Stomsoft */
168369800Stomsoftstatic void
168469800Stomsoftupdclst(int block)
1685114067Sschweikh{
168669800Stomsoft	DBG_FUNC("updclst")
1687232548Strasz	static int lcs = 0;
168869800Stomsoft
168969800Stomsoft	DBG_ENTER;
169069800Stomsoft
1691232548Strasz	if (sblock.fs_contigsumsize < 1) /* no clustering */
169269800Stomsoft		return;
169369800Stomsoft	/*
169469800Stomsoft	 * update cluster allocation map
169569800Stomsoft	 */
169669800Stomsoft	setbit(cg_clustersfree(&acg), block);
169769800Stomsoft
169869800Stomsoft	/*
169969800Stomsoft	 * update cluster summary table
170069800Stomsoft	 */
1701232548Strasz	if (!lcs) {
170269800Stomsoft		/*
170369800Stomsoft		 * calculate size for the trailing cluster
170469800Stomsoft		 */
1705232548Strasz		for (block--; lcs < sblock.fs_contigsumsize; block--, lcs++ ) {
1706232548Strasz			if (isclr(cg_clustersfree(&acg), block))
170769800Stomsoft				break;
170869800Stomsoft		}
1709114067Sschweikh	}
1710232548Strasz	if (lcs < sblock.fs_contigsumsize) {
1711232548Strasz		if (lcs)
171269800Stomsoft			cg_clustersum(&acg)[lcs]--;
171369800Stomsoft		lcs++;
171469800Stomsoft		cg_clustersum(&acg)[lcs]++;
171569800Stomsoft	}
171669800Stomsoft
171769800Stomsoft	DBG_LEAVE;
171869800Stomsoft	return;
171969800Stomsoft}
1720234846Strasz
1721234846Straszstatic void
1722234846Straszmount_reload(const struct statfs *stfs)
1723234846Strasz{
1724234846Strasz	char errmsg[255];
1725234846Strasz	struct iovec *iov;
1726234846Strasz	int iovlen;
1727234846Strasz
1728234846Strasz	iov = NULL;
1729234846Strasz	iovlen = 0;
1730234846Strasz	*errmsg = '\0';
1731234846Strasz	build_iovec(&iov, &iovlen, "fstype", __DECONST(char *, "ffs"), 4);
1732234846Strasz	build_iovec(&iov, &iovlen, "fspath", __DECONST(char *, stfs->f_mntonname), (size_t)-1);
1733234846Strasz	build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
1734234846Strasz	build_iovec(&iov, &iovlen, "update", NULL, 0);
1735234846Strasz	build_iovec(&iov, &iovlen, "reload", NULL, 0);
1736234846Strasz
1737234846Strasz	if (nmount(iov, iovlen, stfs->f_flags) < 0) {
1738234846Strasz		errmsg[sizeof(errmsg) - 1] = '\0';
1739234846Strasz		err(9, "%s: cannot reload filesystem%s%s", stfs->f_mntonname,
1740234846Strasz		    *errmsg != '\0' ? ": " : "", errmsg);
1741234846Strasz	}
1742234846Strasz}
1743