growfs.c revision 243246
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: head/sbin/growfs/growfs.c 243246 2012-11-18 19:01:00Z trasz $");
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];
144234314Strasz	static int randinit = 0;
14569800Stomsoft
14669800Stomsoft	DBG_ENTER;
14769800Stomsoft
14869800Stomsoft	if (!randinit) {
14969800Stomsoft		randinit = 1;
15069800Stomsoft		srandomdev();
15169800Stomsoft	}
152217726Smarcel	time(&modtime);
15369800Stomsoft
15469800Stomsoft	/*
15569800Stomsoft	 * Get the cylinder summary into the memory.
15669800Stomsoft	 */
15777885Stomsoft	fscs = (struct csum *)calloc((size_t)1, (size_t)sblock.fs_cssize);
158232548Strasz	if (fscs == NULL)
15969926Stomsoft		errx(1, "calloc failed");
16069800Stomsoft	for (i = 0; i < osblock.fs_cssize; i += osblock.fs_bsize) {
16169800Stomsoft		rdfs(fsbtodb(&osblock, osblock.fs_csaddr +
16277885Stomsoft		    numfrags(&osblock, i)), (size_t)MIN(osblock.fs_cssize - i,
163232548Strasz		    osblock.fs_bsize), (void *)(((char *)fscs) + i), fsi);
16469800Stomsoft	}
16569800Stomsoft
16669800Stomsoft#ifdef FS_DEBUG
167232548Strasz	{
168232548Strasz		struct csum *dbg_csp;
169232548Strasz		int dbg_csc;
170232548Strasz		char dbg_line[80];
17169800Stomsoft
172232548Strasz		dbg_csp = fscs;
173232548Strasz
174232548Strasz		for (dbg_csc = 0; dbg_csc < osblock.fs_ncg; dbg_csc++) {
175232548Strasz			snprintf(dbg_line, sizeof(dbg_line),
176232548Strasz			    "%d. old csum in old location", dbg_csc);
177232548Strasz			DBG_DUMP_CSUM(&osblock, dbg_line, dbg_csp++);
178232548Strasz		}
17969800Stomsoft	}
18069800Stomsoft#endif /* FS_DEBUG */
18169800Stomsoft	DBG_PRINT0("fscs read\n");
18269800Stomsoft
18369800Stomsoft	/*
18469800Stomsoft	 * Do all needed changes in the former last cylinder group.
18569800Stomsoft	 */
186232548Strasz	updjcg(osblock.fs_ncg - 1, modtime, fsi, fso, Nflag);
18769800Stomsoft
18869800Stomsoft	/*
189223652Strasz	 * Dump out summary information about file system.
19069800Stomsoft	 */
191234846Strasz#ifdef FS_DEBUG
192234314Strasz#define B2MBFACTOR (1 / (1024.0 * 1024.0))
193127816Smux	printf("growfs: %.1fMB (%jd sectors) block size %d, fragment size %d\n",
19469800Stomsoft	    (float)sblock.fs_size * sblock.fs_fsize * B2MBFACTOR,
195127816Smux	    (intmax_t)fsbtodb(&sblock, sblock.fs_size), sblock.fs_bsize,
196127816Smux	    sblock.fs_fsize);
19798542Smckusick	printf("\tusing %d cylinder groups of %.2fMB, %d blks, %d inodes.\n",
19898542Smckusick	    sblock.fs_ncg, (float)sblock.fs_fpg * sblock.fs_fsize * B2MBFACTOR,
19998542Smckusick	    sblock.fs_fpg / sblock.fs_frag, sblock.fs_ipg);
20098542Smckusick	if (sblock.fs_flags & FS_DOSOFTDEP)
20198542Smckusick		printf("\twith soft updates\n");
202234314Strasz#undef B2MBFACTOR
203234846Strasz#endif /* FS_DEBUG */
20469800Stomsoft
20569800Stomsoft	/*
20669800Stomsoft	 * Now build the cylinders group blocks and
20769800Stomsoft	 * then print out indices of cylinder groups.
20869800Stomsoft	 */
20969800Stomsoft	printf("super-block backups (for fsck -b #) at:\n");
21069800Stomsoft	i = 0;
21169800Stomsoft	width = charsperline();
21269800Stomsoft
21369800Stomsoft	/*
21469800Stomsoft	 * Iterate for only the new cylinder groups.
21569800Stomsoft	 */
21669800Stomsoft	for (cylno = osblock.fs_ncg; cylno < sblock.fs_ncg; cylno++) {
217217726Smarcel		initcg(cylno, modtime, fso, Nflag);
218174706Sdas		j = sprintf(tmpbuf, " %jd%s",
219174706Sdas		    (intmax_t)fsbtodb(&sblock, cgsblock(&sblock, cylno)),
220232548Strasz		    cylno < (sblock.fs_ncg - 1) ? "," : "" );
22169800Stomsoft		if (i + j >= width) {
22269800Stomsoft			printf("\n");
22369800Stomsoft			i = 0;
22469800Stomsoft		}
22569800Stomsoft		i += j;
22669800Stomsoft		printf("%s", tmpbuf);
22769800Stomsoft		fflush(stdout);
22869800Stomsoft	}
22969800Stomsoft	printf("\n");
23069800Stomsoft
23169800Stomsoft	/*
23269800Stomsoft	 * Do all needed changes in the first cylinder group.
23369800Stomsoft	 * allocate blocks in new location
23469800Stomsoft	 */
235217726Smarcel	updcsloc(modtime, fsi, fso, Nflag);
23669800Stomsoft
23769800Stomsoft	/*
23869800Stomsoft	 * Now write the cylinder summary back to disk.
23969800Stomsoft	 */
24069800Stomsoft	for (i = 0; i < sblock.fs_cssize; i += sblock.fs_bsize) {
24169800Stomsoft		wtfs(fsbtodb(&sblock, sblock.fs_csaddr + numfrags(&sblock, i)),
24277885Stomsoft		    (size_t)MIN(sblock.fs_cssize - i, sblock.fs_bsize),
24377885Stomsoft		    (void *)(((char *)fscs) + i), fso, Nflag);
24469800Stomsoft	}
24569800Stomsoft	DBG_PRINT0("fscs written\n");
24669800Stomsoft
24769800Stomsoft#ifdef FS_DEBUG
248232548Strasz	{
249232548Strasz		struct csum	*dbg_csp;
250232548Strasz		int	dbg_csc;
251232548Strasz		char	dbg_line[80];
25269800Stomsoft
253232548Strasz		dbg_csp = fscs;
254232548Strasz		for (dbg_csc = 0; dbg_csc < sblock.fs_ncg; dbg_csc++) {
255232548Strasz			snprintf(dbg_line, sizeof(dbg_line),
256232548Strasz			    "%d. new csum in new location", dbg_csc);
257232548Strasz			DBG_DUMP_CSUM(&sblock, dbg_line, dbg_csp++);
258232548Strasz		}
25969800Stomsoft	}
26069800Stomsoft#endif /* FS_DEBUG */
26169800Stomsoft
26269800Stomsoft	/*
26369800Stomsoft	 * Now write the new superblock back to disk.
26469800Stomsoft	 */
265217726Smarcel	sblock.fs_time = modtime;
26698542Smckusick	wtfs(sblockloc, (size_t)SBLOCKSIZE, (void *)&sblock, fso, Nflag);
26769800Stomsoft	DBG_PRINT0("sblock written\n");
268232548Strasz	DBG_DUMP_FS(&sblock, "new initial sblock");
26969800Stomsoft
27069800Stomsoft	/*
27169800Stomsoft	 * Clean up the dynamic fields in our superblock copies.
27269800Stomsoft	 */
27369800Stomsoft	sblock.fs_fmod = 0;
27469800Stomsoft	sblock.fs_clean = 1;
27569800Stomsoft	sblock.fs_ronly = 0;
27669800Stomsoft	sblock.fs_cgrotor = 0;
27769800Stomsoft	sblock.fs_state = 0;
27869800Stomsoft	memset((void *)&sblock.fs_fsmnt, 0, sizeof(sblock.fs_fsmnt));
27969800Stomsoft	sblock.fs_flags &= FS_DOSOFTDEP;
28069800Stomsoft
28169800Stomsoft	/*
28269800Stomsoft	 * XXX
283114067Sschweikh	 * The following fields are currently distributed from the superblock
28469800Stomsoft	 * to the copies:
28569800Stomsoft	 *     fs_minfree
28669800Stomsoft	 *     fs_rotdelay
28769800Stomsoft	 *     fs_maxcontig
28869800Stomsoft	 *     fs_maxbpg
28969800Stomsoft	 *     fs_minfree,
29069800Stomsoft	 *     fs_optim
29169800Stomsoft	 *     fs_flags regarding SOFTPDATES
29269800Stomsoft	 *
29369800Stomsoft	 * We probably should rather change the summary for the cylinder group
29469800Stomsoft	 * statistics here to the value of what would be in there, if the file
295114067Sschweikh	 * system were created initially with the new size. Therefor we still
29669800Stomsoft	 * need to find an easy way of calculating that.
29769800Stomsoft	 * Possibly we can try to read the first superblock copy and apply the
298114067Sschweikh	 * "diffed" stats between the old and new superblock by still copying
29969800Stomsoft	 * certain parameters onto that.
30069800Stomsoft	 */
30169800Stomsoft
30269800Stomsoft	/*
30369800Stomsoft	 * Write out the duplicate super blocks.
30469800Stomsoft	 */
30569800Stomsoft	for (cylno = 0; cylno < sblock.fs_ncg; cylno++) {
30669800Stomsoft		wtfs(fsbtodb(&sblock, cgsblock(&sblock, cylno)),
30798542Smckusick		    (size_t)SBLOCKSIZE, (void *)&sblock, fso, Nflag);
30869800Stomsoft	}
30969800Stomsoft	DBG_PRINT0("sblock copies written\n");
310232548Strasz	DBG_DUMP_FS(&sblock, "new other sblocks");
31169800Stomsoft
31269800Stomsoft	DBG_LEAVE;
31369800Stomsoft	return;
31469800Stomsoft}
31569800Stomsoft
31669800Stomsoft/*
317114067Sschweikh * This creates a new cylinder group structure, for more details please see
318114067Sschweikh * the source of newfs(8), as this function is taken over almost unchanged.
319114067Sschweikh * As this is never called for the first cylinder group, the special
32069800Stomsoft * provisions for that case are removed here.
32169800Stomsoft */
32269800Stomsoftstatic void
323217726Smarcelinitcg(int cylno, time_t modtime, int fso, unsigned int Nflag)
32469800Stomsoft{
32569800Stomsoft	DBG_FUNC("initcg")
326212839Sbrian	static caddr_t iobuf;
327203770Smckusick	long blkno, start;
328241013Smdf	ino_t ino;
329127798Sle	ufs2_daddr_t i, cbase, dmax;
33098542Smckusick	struct ufs1_dinode *dp1;
33192806Sobrien	struct csum *cs;
332234312Strasz	uint j, d, dupper, dlower;
33369800Stomsoft
334212839Sbrian	if (iobuf == NULL && (iobuf = malloc(sblock.fs_bsize * 3)) == NULL)
33598542Smckusick		errx(37, "panic: cannot allocate I/O buffer");
336212839Sbrian
33769800Stomsoft	/*
33869800Stomsoft	 * Determine block bounds for cylinder group.
33998542Smckusick	 * Allow space for super block summary information in first
34098542Smckusick	 * cylinder group.
34169800Stomsoft	 */
34269800Stomsoft	cbase = cgbase(&sblock, cylno);
34369800Stomsoft	dmax = cbase + sblock.fs_fpg;
34498542Smckusick	if (dmax > sblock.fs_size)
34569800Stomsoft		dmax = sblock.fs_size;
34669800Stomsoft	dlower = cgsblock(&sblock, cylno) - cbase;
34769800Stomsoft	dupper = cgdmin(&sblock, cylno) - cbase;
34898542Smckusick	if (cylno == 0)	/* XXX fscs may be relocated */
34969800Stomsoft		dupper += howmany(sblock.fs_cssize, sblock.fs_fsize);
35098542Smckusick	cs = &fscs[cylno];
35198542Smckusick	memset(&acg, 0, sblock.fs_cgsize);
352217726Smarcel	acg.cg_time = modtime;
35369800Stomsoft	acg.cg_magic = CG_MAGIC;
35469800Stomsoft	acg.cg_cgx = cylno;
35569800Stomsoft	acg.cg_niblk = sblock.fs_ipg;
356212839Sbrian	acg.cg_initediblk = sblock.fs_ipg < 2 * INOPB(&sblock) ?
357212839Sbrian	    sblock.fs_ipg : 2 * INOPB(&sblock);
35869800Stomsoft	acg.cg_ndblk = dmax - cbase;
35998542Smckusick	if (sblock.fs_contigsumsize > 0)
36069800Stomsoft		acg.cg_nclusterblks = acg.cg_ndblk / sblock.fs_frag;
36198542Smckusick	start = &acg.cg_space[0] - (u_char *)(&acg.cg_firstfield);
36298542Smckusick	if (sblock.fs_magic == FS_UFS2_MAGIC) {
36398542Smckusick		acg.cg_iusedoff = start;
36498542Smckusick	} else {
36598542Smckusick		acg.cg_old_ncyl = sblock.fs_old_cpg;
36698542Smckusick		acg.cg_old_time = acg.cg_time;
36798542Smckusick		acg.cg_time = 0;
36898542Smckusick		acg.cg_old_niblk = acg.cg_niblk;
36998542Smckusick		acg.cg_niblk = 0;
370212839Sbrian		acg.cg_initediblk = 0;
37198542Smckusick		acg.cg_old_btotoff = start;
37298542Smckusick		acg.cg_old_boff = acg.cg_old_btotoff +
37398542Smckusick		    sblock.fs_old_cpg * sizeof(int32_t);
37498542Smckusick		acg.cg_iusedoff = acg.cg_old_boff +
37598542Smckusick		    sblock.fs_old_cpg * sizeof(u_int16_t);
37669800Stomsoft	}
377103949Smike	acg.cg_freeoff = acg.cg_iusedoff + howmany(sblock.fs_ipg, CHAR_BIT);
378103949Smike	acg.cg_nextfreeoff = acg.cg_freeoff + howmany(sblock.fs_fpg, CHAR_BIT);
37998542Smckusick	if (sblock.fs_contigsumsize > 0) {
38069800Stomsoft		acg.cg_clustersumoff =
38198542Smckusick		    roundup(acg.cg_nextfreeoff, sizeof(u_int32_t));
38298542Smckusick		acg.cg_clustersumoff -= sizeof(u_int32_t);
38369800Stomsoft		acg.cg_clusteroff = acg.cg_clustersumoff +
38469800Stomsoft		    (sblock.fs_contigsumsize + 1) * sizeof(u_int32_t);
38598542Smckusick		acg.cg_nextfreeoff = acg.cg_clusteroff +
386103949Smike		    howmany(fragstoblks(&sblock, sblock.fs_fpg), CHAR_BIT);
38769800Stomsoft	}
388203770Smckusick	if (acg.cg_nextfreeoff > (unsigned)sblock.fs_cgsize) {
38969800Stomsoft		/*
39098542Smckusick		 * This should never happen as we would have had that panic
391223652Strasz		 * already on file system creation
39269800Stomsoft		 */
39369926Stomsoft		errx(37, "panic: cylinder group too big");
39469800Stomsoft	}
39569800Stomsoft	acg.cg_cs.cs_nifree += sblock.fs_ipg;
39669800Stomsoft	if (cylno == 0)
397241013Smdf		for (ino = 0; ino < ROOTINO; ino++) {
398241013Smdf			setbit(cg_inosused(&acg), ino);
39969800Stomsoft			acg.cg_cs.cs_nifree--;
40069800Stomsoft		}
401136289Sscottl	/*
402223652Strasz	 * For the old file system, we have to initialize all the inodes.
403136289Sscottl	 */
404136289Sscottl	if (sblock.fs_magic == FS_UFS1_MAGIC) {
405136289Sscottl		bzero(iobuf, sblock.fs_bsize);
406203835Sgavin		for (i = 0; i < sblock.fs_ipg / INOPF(&sblock);
407232548Strasz		    i += sblock.fs_frag) {
408212886Smarcel			dp1 = (struct ufs1_dinode *)(void *)iobuf;
409201401Sgavin			for (j = 0; j < INOPB(&sblock); j++) {
410201401Sgavin				dp1->di_gen = random();
411201401Sgavin				dp1++;
412201401Sgavin			}
413136289Sscottl			wtfs(fsbtodb(&sblock, cgimin(&sblock, cylno) + i),
414136289Sscottl			    sblock.fs_bsize, iobuf, fso, Nflag);
415136289Sscottl		}
41669800Stomsoft	}
41798542Smckusick	if (cylno > 0) {
41898542Smckusick		/*
41998542Smckusick		 * In cylno 0, beginning space is reserved
42098542Smckusick		 * for boot and super blocks.
42198542Smckusick		 */
42298542Smckusick		for (d = 0; d < dlower; d += sblock.fs_frag) {
42398542Smckusick			blkno = d / sblock.fs_frag;
42498542Smckusick			setblock(&sblock, cg_blksfree(&acg), blkno);
42598542Smckusick			if (sblock.fs_contigsumsize > 0)
42698542Smckusick				setbit(cg_clustersfree(&acg), blkno);
42798542Smckusick			acg.cg_cs.cs_nbfree++;
42869800Stomsoft		}
42998542Smckusick		sblock.fs_dsize += dlower;
43069800Stomsoft	}
43169800Stomsoft	sblock.fs_dsize += acg.cg_ndblk - dupper;
43269800Stomsoft	if ((i = dupper % sblock.fs_frag)) {
43369800Stomsoft		acg.cg_frsum[sblock.fs_frag - i]++;
43469800Stomsoft		for (d = dupper + sblock.fs_frag - i; dupper < d; dupper++) {
43569800Stomsoft			setbit(cg_blksfree(&acg), dupper);
43669800Stomsoft			acg.cg_cs.cs_nffree++;
43769800Stomsoft		}
43869800Stomsoft	}
43998542Smckusick	for (d = dupper; d + sblock.fs_frag <= acg.cg_ndblk;
440232548Strasz	    d += sblock.fs_frag) {
44169800Stomsoft		blkno = d / sblock.fs_frag;
44269800Stomsoft		setblock(&sblock, cg_blksfree(&acg), blkno);
44398542Smckusick		if (sblock.fs_contigsumsize > 0)
44469800Stomsoft			setbit(cg_clustersfree(&acg), blkno);
44569800Stomsoft		acg.cg_cs.cs_nbfree++;
44669800Stomsoft	}
44798542Smckusick	if (d < acg.cg_ndblk) {
44898542Smckusick		acg.cg_frsum[acg.cg_ndblk - d]++;
44998542Smckusick		for (; d < acg.cg_ndblk; d++) {
45069800Stomsoft			setbit(cg_blksfree(&acg), d);
45169800Stomsoft			acg.cg_cs.cs_nffree++;
45269800Stomsoft		}
45369800Stomsoft	}
45469800Stomsoft	if (sblock.fs_contigsumsize > 0) {
45598542Smckusick		int32_t *sump = cg_clustersum(&acg);
45698542Smckusick		u_char *mapp = cg_clustersfree(&acg);
45798542Smckusick		int map = *mapp++;
45898542Smckusick		int bit = 1;
45998542Smckusick		int run = 0;
46069800Stomsoft
46169800Stomsoft		for (i = 0; i < acg.cg_nclusterblks; i++) {
46298542Smckusick			if ((map & bit) != 0)
46369800Stomsoft				run++;
46498542Smckusick			else if (run != 0) {
46598542Smckusick				if (run > sblock.fs_contigsumsize)
46669800Stomsoft					run = sblock.fs_contigsumsize;
46769800Stomsoft				sump[run]++;
46869800Stomsoft				run = 0;
46969800Stomsoft			}
470103949Smike			if ((i & (CHAR_BIT - 1)) != CHAR_BIT - 1)
47169800Stomsoft				bit <<= 1;
47298542Smckusick			else {
47369800Stomsoft				map = *mapp++;
47469800Stomsoft				bit = 1;
47569800Stomsoft			}
47669800Stomsoft		}
47769800Stomsoft		if (run != 0) {
47898542Smckusick			if (run > sblock.fs_contigsumsize)
47969800Stomsoft				run = sblock.fs_contigsumsize;
48069800Stomsoft			sump[run]++;
48169800Stomsoft		}
48269800Stomsoft	}
48369800Stomsoft	sblock.fs_cstotal.cs_ndir += acg.cg_cs.cs_ndir;
48469800Stomsoft	sblock.fs_cstotal.cs_nffree += acg.cg_cs.cs_nffree;
48569800Stomsoft	sblock.fs_cstotal.cs_nbfree += acg.cg_cs.cs_nbfree;
48669800Stomsoft	sblock.fs_cstotal.cs_nifree += acg.cg_cs.cs_nifree;
48769800Stomsoft	*cs = acg.cg_cs;
488212839Sbrian
489212839Sbrian	memcpy(iobuf, &acg, sblock.fs_cgsize);
490212839Sbrian	memset(iobuf + sblock.fs_cgsize, '\0',
491212839Sbrian	    sblock.fs_bsize * 3 - sblock.fs_cgsize);
492212839Sbrian
49369800Stomsoft	wtfs(fsbtodb(&sblock, cgtod(&sblock, cylno)),
494212839Sbrian	    sblock.fs_bsize * 3, iobuf, fso, Nflag);
495212839Sbrian	DBG_DUMP_CG(&sblock, "new cg", &acg);
49669800Stomsoft
49769800Stomsoft	DBG_LEAVE;
49869800Stomsoft	return;
49969800Stomsoft}
50069800Stomsoft
50169800Stomsoft/*
502114067Sschweikh * Here we add or subtract (sign +1/-1) the available fragments in a given
50369800Stomsoft * block to or from the fragment statistics. By subtracting before and adding
504114067Sschweikh * after an operation on the free frag map we can easy update the fragment
505108470Sschweikh * statistic, which seems to be otherwise a rather complex operation.
50669800Stomsoft */
50769800Stomsoftstatic void
50898542Smckusickfrag_adjust(ufs2_daddr_t frag, int sign)
50969800Stomsoft{
51069800Stomsoft	DBG_FUNC("frag_adjust")
51169800Stomsoft	int fragsize;
51269800Stomsoft	int f;
51369800Stomsoft
51469800Stomsoft	DBG_ENTER;
51569800Stomsoft
516234314Strasz	fragsize = 0;
51769800Stomsoft	/*
51869800Stomsoft	 * Here frag only needs to point to any fragment in the block we want
51969800Stomsoft	 * to examine.
52069800Stomsoft	 */
521232548Strasz	for (f = rounddown(frag, sblock.fs_frag);
522232548Strasz	    f < roundup(frag + 1, sblock.fs_frag); f++) {
52369800Stomsoft		/*
524114067Sschweikh		 * Count contiguous free fragments.
52569800Stomsoft		 */
526232548Strasz		if (isset(cg_blksfree(&acg), f)) {
52769800Stomsoft			fragsize++;
52869800Stomsoft		} else {
529232548Strasz			if (fragsize && fragsize < sblock.fs_frag) {
53069800Stomsoft				/*
53169800Stomsoft				 * We found something in between.
53269800Stomsoft				 */
533234189Strasz				acg.cg_frsum[fragsize] += sign;
53469926Stomsoft				DBG_PRINT2("frag_adjust [%d]+=%d\n",
535232548Strasz				    fragsize, sign);
53669800Stomsoft			}
537232548Strasz			fragsize = 0;
53869800Stomsoft		}
53969800Stomsoft	}
540232548Strasz	if (fragsize && fragsize < sblock.fs_frag) {
54169800Stomsoft		/*
54269800Stomsoft		 * We found something.
54369800Stomsoft		 */
544232548Strasz		acg.cg_frsum[fragsize] += sign;
545232548Strasz		DBG_PRINT2("frag_adjust [%d]+=%d\n", fragsize, sign);
54669800Stomsoft	}
547232548Strasz	DBG_PRINT2("frag_adjust [[%d]]+=%d\n", fragsize, sign);
54869800Stomsoft
54969800Stomsoft	DBG_LEAVE;
55069800Stomsoft	return;
55169800Stomsoft}
55269800Stomsoft
55369800Stomsoft/*
55469800Stomsoft * Here we do all needed work for the former last cylinder group. It has to be
555223652Strasz * changed in any case, even if the file system ended exactly on the end of
556114067Sschweikh * this group, as there is some slightly inconsistent handling of the number
557114067Sschweikh * of cylinders in the cylinder group. We start again by reading the cylinder
55869800Stomsoft * group from disk. If the last block was not fully available, we first handle
559114067Sschweikh * the missing fragments, then we handle all new full blocks in that file
560114067Sschweikh * system and finally we handle the new last fragmented block in the file
561114067Sschweikh * system.  We again have to handle the fragment statistics rotational layout
56269800Stomsoft * tables and cluster summary during all those operations.
56369800Stomsoft */
56469800Stomsoftstatic void
565217726Smarcelupdjcg(int cylno, time_t modtime, int fsi, int fso, unsigned int Nflag)
56669800Stomsoft{
56769800Stomsoft	DBG_FUNC("updjcg")
568232548Strasz	ufs2_daddr_t cbase, dmax, dupper;
569232548Strasz	struct csum *cs;
570232548Strasz	int i, k;
571232548Strasz	int j = 0;
57269800Stomsoft
57369800Stomsoft	DBG_ENTER;
57469800Stomsoft
57569800Stomsoft	/*
57669800Stomsoft	 * Read the former last (joining) cylinder group from disk, and make
57769800Stomsoft	 * a copy.
57869800Stomsoft	 */
57977885Stomsoft	rdfs(fsbtodb(&osblock, cgtod(&osblock, cylno)),
58077885Stomsoft	    (size_t)osblock.fs_cgsize, (void *)&aocg, fsi);
58169800Stomsoft	DBG_PRINT0("jcg read\n");
582232548Strasz	DBG_DUMP_CG(&sblock, "old joining cg", &aocg);
58369800Stomsoft
58469800Stomsoft	memcpy((void *)&cgun1, (void *)&cgun2, sizeof(cgun2));
58569800Stomsoft
58669800Stomsoft	/*
587114067Sschweikh	 * If the cylinder group had already its new final size almost
58869800Stomsoft	 * nothing is to be done ... except:
58969800Stomsoft	 * For some reason the value of cg_ncyl in the last cylinder group has
590114067Sschweikh	 * to be zero instead of fs_cpg. As this is now no longer the last
59169800Stomsoft	 * cylinder group we have to change that value now to fs_cpg.
592114067Sschweikh	 */
59369800Stomsoft
594232548Strasz	if (cgbase(&osblock, cylno + 1) == osblock.fs_size) {
59598542Smckusick		if (sblock.fs_magic == FS_UFS1_MAGIC)
596234314Strasz			acg.cg_old_ncyl = sblock.fs_old_cpg;
59769800Stomsoft
59877885Stomsoft		wtfs(fsbtodb(&sblock, cgtod(&sblock, cylno)),
59977885Stomsoft		    (size_t)sblock.fs_cgsize, (void *)&acg, fso, Nflag);
60069800Stomsoft		DBG_PRINT0("jcg written\n");
601232548Strasz		DBG_DUMP_CG(&sblock, "new joining cg", &acg);
60269800Stomsoft
60369800Stomsoft		DBG_LEAVE;
60469800Stomsoft		return;
60569800Stomsoft	}
60669800Stomsoft
60769800Stomsoft	/*
60869800Stomsoft	 * Set up some variables needed later.
60969800Stomsoft	 */
61069800Stomsoft	cbase = cgbase(&sblock, cylno);
61169800Stomsoft	dmax = cbase + sblock.fs_fpg;
61269800Stomsoft	if (dmax > sblock.fs_size)
61369800Stomsoft		dmax = sblock.fs_size;
61469800Stomsoft	dupper = cgdmin(&sblock, cylno) - cbase;
615232548Strasz	if (cylno == 0) /* XXX fscs may be relocated */
61669800Stomsoft		dupper += howmany(sblock.fs_cssize, sblock.fs_fsize);
61769800Stomsoft
61869800Stomsoft	/*
61969800Stomsoft	 * Set pointer to the cylinder summary for our cylinder group.
62069800Stomsoft	 */
62169800Stomsoft	cs = fscs + cylno;
62269800Stomsoft
62369800Stomsoft	/*
62469800Stomsoft	 * Touch the cylinder group, update all fields in the cylinder group as
62569800Stomsoft	 * needed, update the free space in the superblock.
62669800Stomsoft	 */
627217726Smarcel	acg.cg_time = modtime;
628203770Smckusick	if ((unsigned)cylno == sblock.fs_ncg - 1) {
62969800Stomsoft		/*
63069800Stomsoft		 * This is still the last cylinder group.
63169800Stomsoft		 */
63298542Smckusick		if (sblock.fs_magic == FS_UFS1_MAGIC)
63398542Smckusick			acg.cg_old_ncyl =
63498542Smckusick			    sblock.fs_old_ncyl % sblock.fs_old_cpg;
63569800Stomsoft	} else {
63698542Smckusick		acg.cg_old_ncyl = sblock.fs_old_cpg;
63769800Stomsoft	}
638232548Strasz	DBG_PRINT2("jcg dbg: %d %u", cylno, sblock.fs_ncg);
639127798Sle#ifdef FS_DEBUG
64098542Smckusick	if (sblock.fs_magic == FS_UFS1_MAGIC)
641232548Strasz		DBG_PRINT2("%d %u", acg.cg_old_ncyl, sblock.fs_old_cpg);
642127798Sle#endif
64398542Smckusick	DBG_PRINT0("\n");
64469800Stomsoft	acg.cg_ndblk = dmax - cbase;
645232548Strasz	sblock.fs_dsize += acg.cg_ndblk - aocg.cg_ndblk;
646232548Strasz	if (sblock.fs_contigsumsize > 0)
64769800Stomsoft		acg.cg_nclusterblks = acg.cg_ndblk / sblock.fs_frag;
64869800Stomsoft
64969800Stomsoft	/*
650114067Sschweikh	 * Now we have to update the free fragment bitmap for our new free
651114067Sschweikh	 * space.  There again we have to handle the fragmentation and also
652114067Sschweikh	 * the rotational layout tables and the cluster summary.  This is
653114067Sschweikh	 * also done per fragment for the first new block if the old file
654114067Sschweikh	 * system end was not on a block boundary, per fragment for the new
655223652Strasz	 * last block if the new file system end is not on a block boundary,
65669800Stomsoft	 * and per block for all space in between.
65769800Stomsoft	 *
65869800Stomsoft	 * Handle the first new block here if it was partially available
65969800Stomsoft	 * before.
66069800Stomsoft	 */
661232548Strasz	if (osblock.fs_size % sblock.fs_frag) {
662232548Strasz		if (roundup(osblock.fs_size, sblock.fs_frag) <=
663232548Strasz		    sblock.fs_size) {
66469800Stomsoft			/*
66569800Stomsoft			 * The new space is enough to fill at least this
66669800Stomsoft			 * block
66769800Stomsoft			 */
668232548Strasz			j = 0;
669232548Strasz			for (i = roundup(osblock.fs_size - cbase,
670232548Strasz			    sblock.fs_frag) - 1; i >= osblock.fs_size - cbase;
67169800Stomsoft			    i--) {
67269800Stomsoft				setbit(cg_blksfree(&acg), i);
67369800Stomsoft				acg.cg_cs.cs_nffree++;
67469800Stomsoft				j++;
67569800Stomsoft			}
67669800Stomsoft
67769800Stomsoft			/*
678114067Sschweikh			 * Check if the fragment just created could join an
67969800Stomsoft			 * already existing fragment at the former end of the
680223652Strasz			 * file system.
68169800Stomsoft			 */
682232548Strasz			if (isblock(&sblock, cg_blksfree(&acg),
683232548Strasz			    ((osblock.fs_size - cgbase(&sblock, cylno)) /
684232548Strasz			     sblock.fs_frag))) {
68569800Stomsoft				/*
686114067Sschweikh				 * The block is now completely available.
68769800Stomsoft				 */
68869800Stomsoft				DBG_PRINT0("block was\n");
689232548Strasz				acg.cg_frsum[osblock.fs_size % sblock.fs_frag]--;
69069800Stomsoft				acg.cg_cs.cs_nbfree++;
691232548Strasz				acg.cg_cs.cs_nffree -= sblock.fs_frag;
692232548Strasz				k = rounddown(osblock.fs_size - cbase,
69369800Stomsoft				    sblock.fs_frag);
694232548Strasz				updclst((osblock.fs_size - cbase) /
695232548Strasz				    sblock.fs_frag);
69669800Stomsoft			} else {
69769800Stomsoft				/*
69869800Stomsoft				 * Lets rejoin a possible partially growed
69969800Stomsoft				 * fragment.
70069800Stomsoft				 */
701232548Strasz				k = 0;
702232548Strasz				while (isset(cg_blksfree(&acg), i) &&
703232548Strasz				    (i >= rounddown(osblock.fs_size - cbase,
70469800Stomsoft				    sblock.fs_frag))) {
70569800Stomsoft					i--;
70669800Stomsoft					k++;
70769800Stomsoft				}
708232548Strasz				if (k)
70969800Stomsoft					acg.cg_frsum[k]--;
710232548Strasz				acg.cg_frsum[k + j]++;
71169800Stomsoft			}
71269800Stomsoft		} else {
71369800Stomsoft			/*
71469800Stomsoft			 * We only grow by some fragments within this last
71569800Stomsoft			 * block.
71669800Stomsoft			 */
717232548Strasz			for (i = sblock.fs_size - cbase - 1;
718232548Strasz			    i >= osblock.fs_size - cbase; i--) {
71969800Stomsoft				setbit(cg_blksfree(&acg), i);
72069800Stomsoft				acg.cg_cs.cs_nffree++;
72169800Stomsoft				j++;
72269800Stomsoft			}
72369800Stomsoft			/*
72469800Stomsoft			 * Lets rejoin a possible partially growed fragment.
72569800Stomsoft			 */
726232548Strasz			k = 0;
727232548Strasz			while (isset(cg_blksfree(&acg), i) &&
728232548Strasz			    (i >= rounddown(osblock.fs_size - cbase,
72969800Stomsoft			    sblock.fs_frag))) {
73069800Stomsoft				i--;
73169800Stomsoft				k++;
73269800Stomsoft			}
733232548Strasz			if (k)
73469800Stomsoft				acg.cg_frsum[k]--;
735232548Strasz			acg.cg_frsum[k + j]++;
73669800Stomsoft		}
73769800Stomsoft	}
73869800Stomsoft
73969800Stomsoft	/*
74069800Stomsoft	 * Handle all new complete blocks here.
74169800Stomsoft	 */
742232548Strasz	for (i = roundup(osblock.fs_size - cbase, sblock.fs_frag);
743232548Strasz	    i + sblock.fs_frag <= dmax - cbase;	/* XXX <= or only < ? */
744232548Strasz	    i += sblock.fs_frag) {
74569800Stomsoft		j = i / sblock.fs_frag;
74669800Stomsoft		setblock(&sblock, cg_blksfree(&acg), j);
74769800Stomsoft		updclst(j);
74869800Stomsoft		acg.cg_cs.cs_nbfree++;
74969800Stomsoft	}
75069800Stomsoft
75169800Stomsoft	/*
75269800Stomsoft	 * Handle the last new block if there are stll some new fragments left.
753114067Sschweikh	 * Here we don't have to bother about the cluster summary or the even
75469800Stomsoft	 * the rotational layout table.
75569800Stomsoft	 */
75669800Stomsoft	if (i < (dmax - cbase)) {
75769800Stomsoft		acg.cg_frsum[dmax - cbase - i]++;
75869800Stomsoft		for (; i < dmax - cbase; i++) {
75969800Stomsoft			setbit(cg_blksfree(&acg), i);
76069800Stomsoft			acg.cg_cs.cs_nffree++;
76169800Stomsoft		}
76269800Stomsoft	}
76369800Stomsoft
76469800Stomsoft	sblock.fs_cstotal.cs_nffree +=
76569800Stomsoft	    (acg.cg_cs.cs_nffree - aocg.cg_cs.cs_nffree);
76669800Stomsoft	sblock.fs_cstotal.cs_nbfree +=
76769800Stomsoft	    (acg.cg_cs.cs_nbfree - aocg.cg_cs.cs_nbfree);
76869800Stomsoft	/*
76969800Stomsoft	 * The following statistics are not changed here:
77069800Stomsoft	 *     sblock.fs_cstotal.cs_ndir
77169800Stomsoft	 *     sblock.fs_cstotal.cs_nifree
77269800Stomsoft	 * As the statistics for this cylinder group are ready, copy it to
77369800Stomsoft	 * the summary information array.
77469800Stomsoft	 */
77569800Stomsoft	*cs = acg.cg_cs;
77669800Stomsoft
77769800Stomsoft	/*
77869800Stomsoft	 * Write the updated "joining" cylinder group back to disk.
77969800Stomsoft	 */
78077885Stomsoft	wtfs(fsbtodb(&sblock, cgtod(&sblock, cylno)), (size_t)sblock.fs_cgsize,
78177885Stomsoft	    (void *)&acg, fso, Nflag);
78269800Stomsoft	DBG_PRINT0("jcg written\n");
783232548Strasz	DBG_DUMP_CG(&sblock, "new joining cg", &acg);
78469800Stomsoft
78569800Stomsoft	DBG_LEAVE;
78669800Stomsoft	return;
78769800Stomsoft}
78869800Stomsoft
78969800Stomsoft/*
790114067Sschweikh * Here we update the location of the cylinder summary. We have two possible
791234846Strasz * ways of growing the cylinder summary:
792114067Sschweikh * (1)	We can try to grow the summary in the current location, and relocate
79369800Stomsoft *	possibly used blocks within the current cylinder group.
79469800Stomsoft * (2)	Alternatively we can relocate the whole cylinder summary to the first
795114067Sschweikh *	new completely empty cylinder group. Once the cylinder summary is no
796114067Sschweikh *	longer in the beginning of the first cylinder group you should never
797114067Sschweikh *	use a version of fsck which is not aware of the possibility to have
79869800Stomsoft *	this structure in a non standard place.
799234178Strasz * Option (2) is considered to be less intrusive to the structure of the file-
800234178Strasz * system, so that's the one being used.
80169800Stomsoft */
80269800Stomsoftstatic void
803217726Smarcelupdcsloc(time_t modtime, int fsi, int fso, unsigned int Nflag)
80469800Stomsoft{
80569800Stomsoft	DBG_FUNC("updcsloc")
806232548Strasz	struct csum *cs;
807232548Strasz	int ocscg, ncscg;
808234178Strasz	ufs2_daddr_t d;
809232548Strasz	int lcs = 0;
810232548Strasz	int block;
81169800Stomsoft
81269800Stomsoft	DBG_ENTER;
81369800Stomsoft
814232548Strasz	if (howmany(sblock.fs_cssize, sblock.fs_fsize) ==
81569800Stomsoft	    howmany(osblock.fs_cssize, osblock.fs_fsize)) {
81669800Stomsoft		/*
81769800Stomsoft		 * No new fragment needed.
81869800Stomsoft		 */
81969800Stomsoft		DBG_LEAVE;
82069800Stomsoft		return;
82169800Stomsoft	}
822232548Strasz	ocscg = dtog(&osblock, osblock.fs_csaddr);
823232548Strasz	cs = fscs + ocscg;
82469800Stomsoft
82569800Stomsoft	/*
82669800Stomsoft	 * Read original cylinder group from disk, and make a copy.
82781311Schm	 * XXX	If Nflag is set in some very rare cases we now miss
82881311Schm	 *	some changes done in updjcg by reading the unmodified
82981311Schm	 *	block from disk.
83069800Stomsoft	 */
83177885Stomsoft	rdfs(fsbtodb(&osblock, cgtod(&osblock, ocscg)),
83277885Stomsoft	    (size_t)osblock.fs_cgsize, (void *)&aocg, fsi);
83369800Stomsoft	DBG_PRINT0("oscg read\n");
834232548Strasz	DBG_DUMP_CG(&sblock, "old summary cg", &aocg);
83569800Stomsoft
83669800Stomsoft	memcpy((void *)&cgun1, (void *)&cgun2, sizeof(cgun2));
83769800Stomsoft
83869800Stomsoft	/*
83969800Stomsoft	 * Touch the cylinder group, set up local variables needed later
84069800Stomsoft	 * and update the superblock.
84169800Stomsoft	 */
842217726Smarcel	acg.cg_time = modtime;
84369800Stomsoft
84469800Stomsoft	/*
84569800Stomsoft	 * XXX	In the case of having active snapshots we may need much more
846114067Sschweikh	 *	blocks for the copy on write. We need each block twice, and
847114067Sschweikh	 *	also up to 8*3 blocks for indirect blocks for all possible
84869800Stomsoft	 *	references.
84969800Stomsoft	 */
850234178Strasz	/*
851234178Strasz	 * There is not enough space in the old cylinder group to
852234178Strasz	 * relocate all blocks as needed, so we relocate the whole
853234178Strasz	 * cylinder group summary to a new group. We try to use the
854234178Strasz	 * first complete new cylinder group just created. Within the
855234178Strasz	 * cylinder group we align the area immediately after the
856234178Strasz	 * cylinder group information location in order to be as
857234178Strasz	 * close as possible to the original implementation of ffs.
858234178Strasz	 *
859234178Strasz	 * First we have to make sure we'll find enough space in the
860234178Strasz	 * new cylinder group. If not, then we currently give up.
861234178Strasz	 * We start with freeing everything which was used by the
862234178Strasz	 * fragments of the old cylinder summary in the current group.
863234178Strasz	 * Now we write back the group meta data, read in the needed
864234178Strasz	 * meta data from the new cylinder group, and start allocating
865234178Strasz	 * within that group. Here we can assume, the group to be
866234178Strasz	 * completely empty. Which makes the handling of fragments and
867234178Strasz	 * clusters a lot easier.
868234178Strasz	 */
869234178Strasz	DBG_TRC;
870234178Strasz	if (sblock.fs_ncg - osblock.fs_ncg < 2)
871234178Strasz		errx(2, "panic: not enough space");
87269800Stomsoft
873234178Strasz	/*
874234178Strasz	 * Point "d" to the first fragment not used by the cylinder
875234178Strasz	 * summary.
876234178Strasz	 */
877234178Strasz	d = osblock.fs_csaddr + (osblock.fs_cssize / osblock.fs_fsize);
87869800Stomsoft
879234178Strasz	/*
880234178Strasz	 * Set up last cluster size ("lcs") already here. Calculate
881234178Strasz	 * the size for the trailing cluster just behind where "d"
882234178Strasz	 * points to.
883234178Strasz	 */
884234178Strasz	if (sblock.fs_contigsumsize > 0) {
885234178Strasz		for (block = howmany(d % sblock.fs_fpg, sblock.fs_frag),
886234189Strasz		    lcs = 0; lcs < sblock.fs_contigsumsize; block++, lcs++) {
887234178Strasz			if (isclr(cg_clustersfree(&acg), block))
888234178Strasz				break;
889234178Strasz		}
890234178Strasz	}
891234178Strasz
892234178Strasz	/*
893234178Strasz	 * Point "d" to the last frag used by the cylinder summary.
894234178Strasz	 */
895234178Strasz	d--;
896234178Strasz
897234178Strasz	DBG_PRINT1("d=%jd\n", (intmax_t)d);
898234178Strasz	if ((d + 1) % sblock.fs_frag) {
89969800Stomsoft		/*
900234178Strasz		 * The end of the cylinder summary is not a complete
901234178Strasz		 * block.
90269800Stomsoft		 */
903234178Strasz		DBG_TRC;
904234178Strasz		frag_adjust(d % sblock.fs_fpg, -1);
905234178Strasz		for (; (d + 1) % sblock.fs_frag; d--) {
906234178Strasz			DBG_PRINT1("d=%jd\n", (intmax_t)d);
907234178Strasz			setbit(cg_blksfree(&acg), d % sblock.fs_fpg);
908234178Strasz			acg.cg_cs.cs_nffree++;
909234178Strasz			sblock.fs_cstotal.cs_nffree++;
91069800Stomsoft		}
91169800Stomsoft		/*
912234178Strasz		 * Point "d" to the last fragment of the last
913234178Strasz		 * (incomplete) block of the cylinder summary.
91469800Stomsoft		 */
915234178Strasz		d++;
916234189Strasz		frag_adjust(d % sblock.fs_fpg, 1);
91769800Stomsoft
918234178Strasz		if (isblock(&sblock, cg_blksfree(&acg),
919234178Strasz		    (d % sblock.fs_fpg) / sblock.fs_frag)) {
920127798Sle			DBG_PRINT1("d=%jd\n", (intmax_t)d);
921234178Strasz			acg.cg_cs.cs_nffree -= sblock.fs_frag;
92269800Stomsoft			acg.cg_cs.cs_nbfree++;
923234178Strasz			sblock.fs_cstotal.cs_nffree -= sblock.fs_frag;
92469800Stomsoft			sblock.fs_cstotal.cs_nbfree++;
925232548Strasz			if (sblock.fs_contigsumsize > 0) {
92669800Stomsoft				setbit(cg_clustersfree(&acg),
927234189Strasz				    (d % sblock.fs_fpg) / sblock.fs_frag);
928232548Strasz				if (lcs < sblock.fs_contigsumsize) {
929232548Strasz					if (lcs)
93069800Stomsoft						cg_clustersum(&acg)[lcs]--;
93169800Stomsoft					lcs++;
93269800Stomsoft					cg_clustersum(&acg)[lcs]++;
93369800Stomsoft				}
93469800Stomsoft			}
93569800Stomsoft		}
93669800Stomsoft		/*
937234178Strasz		 * Point "d" to the first fragment of the block before
938234178Strasz		 * the last incomplete block.
93969800Stomsoft		 */
940234178Strasz		d--;
941234178Strasz	}
94269800Stomsoft
943234178Strasz	DBG_PRINT1("d=%jd\n", (intmax_t)d);
944234178Strasz	for (d = rounddown(d, sblock.fs_frag); d >= osblock.fs_csaddr;
945234178Strasz	    d -= sblock.fs_frag) {
946234178Strasz		DBG_TRC;
947234178Strasz		DBG_PRINT1("d=%jd\n", (intmax_t)d);
948234178Strasz		setblock(&sblock, cg_blksfree(&acg),
949234178Strasz		    (d % sblock.fs_fpg) / sblock.fs_frag);
950234178Strasz		acg.cg_cs.cs_nbfree++;
951234178Strasz		sblock.fs_cstotal.cs_nbfree++;
952234178Strasz		if (sblock.fs_contigsumsize > 0) {
953234178Strasz			setbit(cg_clustersfree(&acg),
954232548Strasz			    (d % sblock.fs_fpg) / sblock.fs_frag);
955234178Strasz			/*
956234178Strasz			 * The last cluster size is already set up.
957234178Strasz			 */
958234178Strasz			if (lcs < sblock.fs_contigsumsize) {
959234178Strasz				if (lcs)
960234178Strasz					cg_clustersum(&acg)[lcs]--;
961234178Strasz				lcs++;
962234178Strasz				cg_clustersum(&acg)[lcs]++;
96369800Stomsoft			}
96469800Stomsoft		}
965234178Strasz	}
966234178Strasz	*cs = acg.cg_cs;
96769800Stomsoft
96869800Stomsoft	/*
969234178Strasz	 * Now write the former cylinder group containing the cylinder
970234178Strasz	 * summary back to disk.
97169800Stomsoft	 */
972234178Strasz	wtfs(fsbtodb(&sblock, cgtod(&sblock, ocscg)),
973234178Strasz	    (size_t)sblock.fs_cgsize, (void *)&acg, fso, Nflag);
974234178Strasz	DBG_PRINT0("oscg written\n");
975234178Strasz	DBG_DUMP_CG(&sblock, "old summary cg", &acg);
97669800Stomsoft
97769800Stomsoft	/*
978234178Strasz	 * Find the beginning of the new cylinder group containing the
979234178Strasz	 * cylinder summary.
98069800Stomsoft	 */
981234178Strasz	sblock.fs_csaddr = cgdmin(&sblock, osblock.fs_ncg);
982234178Strasz	ncscg = dtog(&sblock, sblock.fs_csaddr);
983234178Strasz	cs = fscs + ncscg;
98469800Stomsoft
98569800Stomsoft	/*
986234178Strasz	 * If Nflag is specified, we would now read random data instead
987234178Strasz	 * of an empty cg structure from disk. So we can't simulate that
988234178Strasz	 * part for now.
98969800Stomsoft	 */
990234178Strasz	if (Nflag) {
991234178Strasz		DBG_PRINT0("nscg update skipped\n");
992234178Strasz		DBG_LEAVE;
993234178Strasz		return;
99469800Stomsoft	}
99569800Stomsoft
99669800Stomsoft	/*
997234178Strasz	 * Read the future cylinder group containing the cylinder
998234178Strasz	 * summary from disk, and make a copy.
99969800Stomsoft	 */
1000234178Strasz	rdfs(fsbtodb(&sblock, cgtod(&sblock, ncscg)),
1001234178Strasz	    (size_t)sblock.fs_cgsize, (void *)&aocg, fsi);
1002234178Strasz	DBG_PRINT0("nscg read\n");
1003234178Strasz	DBG_DUMP_CG(&sblock, "new summary cg", &aocg);
100469800Stomsoft
1005234178Strasz	memcpy((void *)&cgun1, (void *)&cgun2, sizeof(cgun2));
1006234178Strasz
100769800Stomsoft	/*
1008234178Strasz	 * Allocate all complete blocks used by the new cylinder
1009234178Strasz	 * summary.
101069800Stomsoft	 */
1011234178Strasz	for (d = sblock.fs_csaddr; d + sblock.fs_frag <=
1012234178Strasz	    sblock.fs_csaddr + (sblock.fs_cssize / sblock.fs_fsize);
1013234178Strasz	    d += sblock.fs_frag) {
1014234178Strasz		clrblock(&sblock, cg_blksfree(&acg),
1015234178Strasz		    (d % sblock.fs_fpg) / sblock.fs_frag);
1016234178Strasz		acg.cg_cs.cs_nbfree--;
1017234178Strasz		sblock.fs_cstotal.cs_nbfree--;
1018234178Strasz		if (sblock.fs_contigsumsize > 0) {
1019234178Strasz			clrbit(cg_clustersfree(&acg),
1020234178Strasz			    (d % sblock.fs_fpg) / sblock.fs_frag);
102169800Stomsoft		}
102269800Stomsoft	}
102369800Stomsoft
102469800Stomsoft	/*
1025234178Strasz	 * Allocate all fragments used by the cylinder summary in the
1026234178Strasz	 * last block.
102769800Stomsoft	 */
1028234189Strasz	if (d < sblock.fs_csaddr + (sblock.fs_cssize / sblock.fs_fsize)) {
1029234178Strasz		for (; d - sblock.fs_csaddr <
1030234178Strasz		    sblock.fs_cssize/sblock.fs_fsize; d++) {
1031234178Strasz			clrbit(cg_blksfree(&acg), d % sblock.fs_fpg);
1032234178Strasz			acg.cg_cs.cs_nffree--;
1033234178Strasz			sblock.fs_cstotal.cs_nffree--;
103469800Stomsoft		}
1035234178Strasz		acg.cg_cs.cs_nbfree--;
1036234178Strasz		acg.cg_cs.cs_nffree += sblock.fs_frag;
1037234178Strasz		sblock.fs_cstotal.cs_nbfree--;
1038234178Strasz		sblock.fs_cstotal.cs_nffree += sblock.fs_frag;
1039234178Strasz		if (sblock.fs_contigsumsize > 0)
1040234178Strasz			clrbit(cg_clustersfree(&acg),
1041234178Strasz			    (d % sblock.fs_fpg) / sblock.fs_frag);
104269800Stomsoft
1043234178Strasz		frag_adjust(d % sblock.fs_fpg, 1);
104469800Stomsoft	}
104569800Stomsoft	/*
1046234178Strasz	 * XXX	Handle the cluster statistics here in the case this
1047234178Strasz	 *	cylinder group is now almost full, and the remaining
1048234178Strasz	 *	space is less then the maximum cluster size. This is
1049234178Strasz	 *	probably not needed, as you would hardly find a file
1050234178Strasz	 *	system which has only MAXCSBUFS+FS_MAXCONTIG of free
1051234178Strasz	 *	space right behind the cylinder group information in
1052234178Strasz	 *	any new cylinder group.
105369800Stomsoft	 */
105469800Stomsoft
1055234178Strasz	/*
1056234178Strasz	 * Update our statistics in the cylinder summary.
1057234178Strasz	 */
105869800Stomsoft	*cs = acg.cg_cs;
105969800Stomsoft
106069800Stomsoft	/*
1061234178Strasz	 * Write the new cylinder group containing the cylinder summary
1062234178Strasz	 * back to disk.
106369800Stomsoft	 */
1064234178Strasz	wtfs(fsbtodb(&sblock, cgtod(&sblock, ncscg)),
1065234178Strasz	    (size_t)sblock.fs_cgsize, (void *)&acg, fso, Nflag);
1066234178Strasz	DBG_PRINT0("nscg written\n");
1067232548Strasz	DBG_DUMP_CG(&sblock, "new summary cg", &acg);
106869800Stomsoft
106969800Stomsoft	DBG_LEAVE;
107069800Stomsoft	return;
107169800Stomsoft}
107269800Stomsoft
107369800Stomsoft/*
107469800Stomsoft * Here we read some block(s) from disk.
107569800Stomsoft */
107669800Stomsoftstatic void
107798542Smckusickrdfs(ufs2_daddr_t bno, size_t size, void *bf, int fsi)
107869800Stomsoft{
107969800Stomsoft	DBG_FUNC("rdfs")
108077885Stomsoft	ssize_t	n;
108169800Stomsoft
108269800Stomsoft	DBG_ENTER;
108369800Stomsoft
1084232548Strasz	if (bno < 0)
1085140351Scharnier		err(32, "rdfs: attempting to read negative block number");
1086232548Strasz	if (lseek(fsi, (off_t)bno * DEV_BSIZE, 0) < 0)
1087127798Sle		err(33, "rdfs: seek error: %jd", (intmax_t)bno);
108877885Stomsoft	n = read(fsi, bf, size);
1089232548Strasz	if (n != (ssize_t)size)
1090127798Sle		err(34, "rdfs: read error: %jd", (intmax_t)bno);
109169800Stomsoft
109269800Stomsoft	DBG_LEAVE;
109369800Stomsoft	return;
109469800Stomsoft}
109569800Stomsoft
109669800Stomsoft/*
109769800Stomsoft * Here we write some block(s) to disk.
109869800Stomsoft */
109969800Stomsoftstatic void
110098542Smckusickwtfs(ufs2_daddr_t bno, size_t size, void *bf, int fso, unsigned int Nflag)
110169800Stomsoft{
110269800Stomsoft	DBG_FUNC("wtfs")
110377885Stomsoft	ssize_t	n;
110469800Stomsoft
110569800Stomsoft	DBG_ENTER;
110669800Stomsoft
110769800Stomsoft	if (Nflag) {
110869800Stomsoft		DBG_LEAVE;
110969800Stomsoft		return;
111069800Stomsoft	}
1111232548Strasz	if (lseek(fso, (off_t)bno * DEV_BSIZE, SEEK_SET) < 0)
111269926Stomsoft		err(35, "wtfs: seek error: %ld", (long)bno);
111377885Stomsoft	n = write(fso, bf, size);
1114232548Strasz	if (n != (ssize_t)size)
111569926Stomsoft		err(36, "wtfs: write error: %ld", (long)bno);
111669800Stomsoft
111769800Stomsoft	DBG_LEAVE;
111869800Stomsoft	return;
111969800Stomsoft}
112069800Stomsoft
112169800Stomsoft/*
1122114067Sschweikh * Here we check if all frags of a block are free. For more details again
112369800Stomsoft * please see the source of newfs(8), as this function is taken over almost
112469800Stomsoft * unchanged.
112569800Stomsoft */
112669800Stomsoftstatic int
112769800Stomsoftisblock(struct fs *fs, unsigned char *cp, int h)
112869800Stomsoft{
112969800Stomsoft	DBG_FUNC("isblock")
1130232548Strasz	unsigned char mask;
113169800Stomsoft
113269800Stomsoft	DBG_ENTER;
113369800Stomsoft
113469800Stomsoft	switch (fs->fs_frag) {
113569800Stomsoft	case 8:
113669800Stomsoft		DBG_LEAVE;
113769800Stomsoft		return (cp[h] == 0xff);
113869800Stomsoft	case 4:
113969800Stomsoft		mask = 0x0f << ((h & 0x1) << 2);
114069800Stomsoft		DBG_LEAVE;
114169800Stomsoft		return ((cp[h >> 1] & mask) == mask);
114269800Stomsoft	case 2:
114369800Stomsoft		mask = 0x03 << ((h & 0x3) << 1);
114469800Stomsoft		DBG_LEAVE;
114569800Stomsoft		return ((cp[h >> 2] & mask) == mask);
114669800Stomsoft	case 1:
114769800Stomsoft		mask = 0x01 << (h & 0x7);
114869800Stomsoft		DBG_LEAVE;
114969800Stomsoft		return ((cp[h >> 3] & mask) == mask);
115069800Stomsoft	default:
115169800Stomsoft		fprintf(stderr, "isblock bad fs_frag %d\n", fs->fs_frag);
115269800Stomsoft		DBG_LEAVE;
115369800Stomsoft		return (0);
115469800Stomsoft	}
115569800Stomsoft}
115669800Stomsoft
115769800Stomsoft/*
115869800Stomsoft * Here we allocate a complete block in the block map. For more details again
1159114067Sschweikh * please see the source of newfs(8), as this function is taken over almost
116069800Stomsoft * unchanged.
116169800Stomsoft */
116269800Stomsoftstatic void
116369800Stomsoftclrblock(struct fs *fs, unsigned char *cp, int h)
116469800Stomsoft{
116569800Stomsoft	DBG_FUNC("clrblock")
116669800Stomsoft
116769800Stomsoft	DBG_ENTER;
116869800Stomsoft
116969800Stomsoft	switch ((fs)->fs_frag) {
117069800Stomsoft	case 8:
117169800Stomsoft		cp[h] = 0;
117269800Stomsoft		break;
117369800Stomsoft	case 4:
117469800Stomsoft		cp[h >> 1] &= ~(0x0f << ((h & 0x1) << 2));
117569800Stomsoft		break;
117669800Stomsoft	case 2:
117769800Stomsoft		cp[h >> 2] &= ~(0x03 << ((h & 0x3) << 1));
117869800Stomsoft		break;
117969800Stomsoft	case 1:
118069800Stomsoft		cp[h >> 3] &= ~(0x01 << (h & 0x7));
118169800Stomsoft		break;
118269800Stomsoft	default:
118369926Stomsoft		warnx("clrblock bad fs_frag %d", fs->fs_frag);
118469800Stomsoft		break;
118569800Stomsoft	}
118669800Stomsoft
118769800Stomsoft	DBG_LEAVE;
118869800Stomsoft	return;
118969800Stomsoft}
119069800Stomsoft
119169800Stomsoft/*
119269800Stomsoft * Here we free a complete block in the free block map. For more details again
1193114067Sschweikh * please see the source of newfs(8), as this function is taken over almost
119469800Stomsoft * unchanged.
119569800Stomsoft */
119669800Stomsoftstatic void
119769800Stomsoftsetblock(struct fs *fs, unsigned char *cp, int h)
119869800Stomsoft{
119969800Stomsoft	DBG_FUNC("setblock")
120069800Stomsoft
120169800Stomsoft	DBG_ENTER;
120269800Stomsoft
120369800Stomsoft	switch (fs->fs_frag) {
120469800Stomsoft	case 8:
120569800Stomsoft		cp[h] = 0xff;
120669800Stomsoft		break;
120769800Stomsoft	case 4:
120869800Stomsoft		cp[h >> 1] |= (0x0f << ((h & 0x1) << 2));
120969800Stomsoft		break;
121069800Stomsoft	case 2:
121169800Stomsoft		cp[h >> 2] |= (0x03 << ((h & 0x3) << 1));
121269800Stomsoft		break;
121369800Stomsoft	case 1:
121469800Stomsoft		cp[h >> 3] |= (0x01 << (h & 0x7));
121569800Stomsoft		break;
121669800Stomsoft	default:
121769926Stomsoft		warnx("setblock bad fs_frag %d", fs->fs_frag);
121869800Stomsoft		break;
121969800Stomsoft	}
122069800Stomsoft
122169800Stomsoft	DBG_LEAVE;
122269800Stomsoft	return;
122369800Stomsoft}
122469800Stomsoft
122569800Stomsoft/*
122669800Stomsoft * Figure out how many lines our current terminal has. For more details again
1227114067Sschweikh * please see the source of newfs(8), as this function is taken over almost
122869800Stomsoft * unchanged.
122969800Stomsoft */
123069800Stomsoftstatic int
123169800Stomsoftcharsperline(void)
123269800Stomsoft{
123369800Stomsoft	DBG_FUNC("charsperline")
1234232548Strasz	int columns;
1235232548Strasz	char *cp;
1236232548Strasz	struct winsize ws;
123769800Stomsoft
123869800Stomsoft	DBG_ENTER;
123969800Stomsoft
124069800Stomsoft	columns = 0;
1241232548Strasz	if (ioctl(0, TIOCGWINSZ, &ws) != -1)
124269800Stomsoft		columns = ws.ws_col;
1243232548Strasz	if (columns == 0 && (cp = getenv("COLUMNS")))
124469800Stomsoft		columns = atoi(cp);
1245232548Strasz	if (columns == 0)
124669800Stomsoft		columns = 80;	/* last resort */
124769800Stomsoft
124869800Stomsoft	DBG_LEAVE;
1249234420Strasz	return (columns);
125069800Stomsoft}
125169800Stomsoft
1252234846Straszstatic int
1253234846Straszis_dev(const char *name)
1254234846Strasz{
1255234846Strasz	struct stat devstat;
1256234846Strasz
1257234846Strasz	if (stat(name, &devstat) != 0)
1258234846Strasz		return (0);
1259234846Strasz	if (!S_ISCHR(devstat.st_mode))
1260234846Strasz		return (0);
1261234846Strasz	return (1);
1262234846Strasz}
1263234846Strasz
1264114936Sgrog/*
1265234846Strasz * Return mountpoint on which the device is currently mounted.
1266234846Strasz */
1267234846Straszstatic const struct statfs *
1268234846Straszdev_to_statfs(const char *dev)
1269114936Sgrog{
1270234846Strasz	struct stat devstat, mntdevstat;
1271234846Strasz	struct statfs *mntbuf, *statfsp;
1272234846Strasz	char device[MAXPATHLEN];
1273234846Strasz	char *mntdevname;
1274234846Strasz	int i, mntsize;
1275114936Sgrog
1276234846Strasz	/*
1277234846Strasz	 * First check the mounted filesystems.
1278234846Strasz	 */
1279234846Strasz	if (stat(dev, &devstat) != 0)
1280234846Strasz		return (NULL);
1281234846Strasz	if (!S_ISCHR(devstat.st_mode) && !S_ISBLK(devstat.st_mode))
1282234846Strasz		return (NULL);
1283114936Sgrog
1284234846Strasz	mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
1285234846Strasz	for (i = 0; i < mntsize; i++) {
1286234846Strasz		statfsp = &mntbuf[i];
1287234846Strasz		mntdevname = statfsp->f_mntfromname;
1288234846Strasz		if (*mntdevname != '/') {
1289234846Strasz			strcpy(device, _PATH_DEV);
1290234846Strasz			strcat(device, mntdevname);
1291234846Strasz			mntdevname = device;
1292234846Strasz		}
1293234846Strasz		if (stat(mntdevname, &mntdevstat) == 0 &&
1294234846Strasz		    mntdevstat.st_rdev == devstat.st_rdev)
1295234846Strasz			return (statfsp);
1296234846Strasz	}
1297114936Sgrog
1298234846Strasz	return (NULL);
1299114936Sgrog}
1300114936Sgrog
1301234846Straszstatic const char *
1302234846Straszmountpoint_to_dev(const char *mountpoint)
1303234846Strasz{
1304234846Strasz	struct statfs *mntbuf, *statfsp;
1305234846Strasz	struct fstab *fs;
1306234846Strasz	int i, mntsize;
1307234846Strasz
1308234846Strasz	/*
1309234846Strasz	 * First check the mounted filesystems.
1310234846Strasz	 */
1311234846Strasz	mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
1312234846Strasz	for (i = 0; i < mntsize; i++) {
1313234846Strasz		statfsp = &mntbuf[i];
1314234846Strasz
1315234846Strasz		if (strcmp(statfsp->f_mntonname, mountpoint) == 0)
1316234846Strasz			return (statfsp->f_mntfromname);
1317234846Strasz	}
1318234846Strasz
1319234846Strasz	/*
1320234846Strasz	 * Check the fstab.
1321234846Strasz	 */
1322234846Strasz	fs = getfsfile(mountpoint);
1323234846Strasz	if (fs != NULL)
1324234846Strasz		return (fs->fs_spec);
1325234846Strasz
1326234846Strasz	return (NULL);
1327234846Strasz}
1328234846Strasz
1329234846Straszstatic const char *
1330234846Straszgetdev(const char *name)
1331234846Strasz{
1332234846Strasz	static char device[MAXPATHLEN];
1333234846Strasz	const char *cp, *dev;
1334234846Strasz
1335234846Strasz	if (is_dev(name))
1336234846Strasz		return (name);
1337234846Strasz
1338234846Strasz	cp = strrchr(name, '/');
1339234846Strasz	if (cp == 0) {
1340234846Strasz		snprintf(device, sizeof(device), "%s%s", _PATH_DEV, name);
1341234846Strasz		if (is_dev(device))
1342234846Strasz			return (device);
1343234846Strasz	}
1344234846Strasz
1345234846Strasz	dev = mountpoint_to_dev(name);
1346234846Strasz	if (dev != NULL && is_dev(dev))
1347234846Strasz		return (dev);
1348234846Strasz
1349234846Strasz	return (NULL);
1350234846Strasz}
1351234846Strasz
135269800Stomsoft/*
1353232548Strasz * growfs(8) is a utility which allows to increase the size of an existing
1354223652Strasz * ufs file system. Currently this can only be done on unmounted file system.
1355114067Sschweikh * It recognizes some command line options to specify the new desired size,
1356223652Strasz * and it does some basic checkings. The old file system size is determined
1357114067Sschweikh * and after some more checks like we can really access the new last block
135869800Stomsoft * on the disk etc. we calculate the new parameters for the superblock. After
1359233656Strasz * having done this we just call growfs() which will do the work.
136069800Stomsoft * We still have to provide support for snapshots. Therefore we first have to
1361114067Sschweikh * understand what data structures are always replicated in the snapshot on
1362114067Sschweikh * creation, for all other blocks we touch during our procedure, we have to
136369926Stomsoft * keep the old blocks unchanged somewhere available for the snapshots. If we
1364114067Sschweikh * are lucky, then we only have to handle our blocks to be relocated in that
136569800Stomsoft * way.
1366114067Sschweikh * Also we have to consider in what order we actually update the critical
1367223652Strasz * data structures of the file system to make sure, that in case of a disaster
136869800Stomsoft * fsck(8) is still able to restore any lost data.
1369114067Sschweikh * The foreseen last step then will be to provide for growing even mounted
1370223652Strasz * file systems. There we have to extend the mount() system call to provide
1371223652Strasz * userland access to the file system locking facility.
137269800Stomsoft */
137369800Stomsoftint
137469800Stomsoftmain(int argc, char **argv)
137569800Stomsoft{
137669800Stomsoft	DBG_FUNC("main")
1377234846Strasz	const char *device;
1378234846Strasz	const struct statfs *statfsp;
1379234846Strasz	uint64_t size = 0;
1380234846Strasz	off_t mediasize;
1381234846Strasz	int error, i, j, fsi, fso, ch, Nflag = 0, yflag = 0;
1382234846Strasz	char *p, reply[5], oldsizebuf[6], newsizebuf[6];
1383234846Strasz	void *testbuf;
138469800Stomsoft
138569800Stomsoft	DBG_ENTER;
138669800Stomsoft
1387232548Strasz	while ((ch = getopt(argc, argv, "Ns:vy")) != -1) {
138869800Stomsoft		switch(ch) {
138969800Stomsoft		case 'N':
1390232548Strasz			Nflag = 1;
139169800Stomsoft			break;
139269800Stomsoft		case 's':
1393234846Strasz			size = (off_t)strtoumax(optarg, &p, 0);
1394234846Strasz			if (p == NULL || *p == '\0')
1395234846Strasz				size *= DEV_BSIZE;
1396234846Strasz			else if (*p == 'b' || *p == 'B')
1397234846Strasz				; /* do nothing */
1398234846Strasz			else if (*p == 'k' || *p == 'K')
1399234846Strasz				size <<= 10;
1400234846Strasz			else if (*p == 'm' || *p == 'M')
1401234846Strasz				size <<= 20;
1402234846Strasz			else if (*p == 'g' || *p == 'G')
1403234846Strasz				size <<= 30;
1404234846Strasz			else if (*p == 't' || *p == 'T') {
1405234846Strasz				size <<= 30;
1406234846Strasz				size <<= 10;
1407234846Strasz			} else
1408234846Strasz				errx(1, "unknown suffix on -s argument");
140969800Stomsoft			break;
141069800Stomsoft		case 'v': /* for compatibility to newfs */
141169800Stomsoft			break;
141269800Stomsoft		case 'y':
1413234846Strasz			yflag = 1;
141469800Stomsoft			break;
141569800Stomsoft		case '?':
141669800Stomsoft			/* FALLTHROUGH */
141769800Stomsoft		default:
141869926Stomsoft			usage();
141969800Stomsoft		}
142069800Stomsoft	}
142169800Stomsoft	argc -= optind;
142269800Stomsoft	argv += optind;
142369800Stomsoft
1424232548Strasz	if (argc != 1)
142569926Stomsoft		usage();
142669800Stomsoft
142769800Stomsoft	/*
1428234846Strasz	 * Now try to guess the device name.
142969800Stomsoft	 */
1430234846Strasz	device = getdev(*argv);
1431234846Strasz	if (device == NULL)
1432234846Strasz		errx(1, "cannot find special device for %s", *argv);
143369800Stomsoft
1434234846Strasz	statfsp = dev_to_statfs(device);
143569800Stomsoft
143669800Stomsoft	fsi = open(device, O_RDONLY);
1437232548Strasz	if (fsi < 0)
143869926Stomsoft		err(1, "%s", device);
143969800Stomsoft
144069800Stomsoft	/*
1441234846Strasz	 * Try to guess the slice size if not specified.
144269800Stomsoft	 */
1443234846Strasz	if (ioctl(fsi, DIOCGMEDIASIZE, &mediasize) == -1)
1444234846Strasz		err(1,"DIOCGMEDIASIZE");
144569800Stomsoft
144669800Stomsoft	/*
1447223652Strasz	 * Check if that partition is suitable for growing a file system.
144869800Stomsoft	 */
1449234846Strasz	if (mediasize < 1)
145069926Stomsoft		errx(1, "partition is unavailable");
145169800Stomsoft
145269800Stomsoft	/*
145369800Stomsoft	 * Read the current superblock, and take a backup.
145469800Stomsoft	 */
145598542Smckusick	for (i = 0; sblock_try[i] != -1; i++) {
145698542Smckusick		sblockloc = sblock_try[i] / DEV_BSIZE;
145798542Smckusick		rdfs(sblockloc, (size_t)SBLOCKSIZE, (void *)&(osblock), fsi);
145898542Smckusick		if ((osblock.fs_magic == FS_UFS1_MAGIC ||
1459232548Strasz		    (osblock.fs_magic == FS_UFS2_MAGIC &&
1460232548Strasz		    osblock.fs_sblockloc == sblock_try[i])) &&
146198542Smckusick		    osblock.fs_bsize <= MAXBSIZE &&
1462127798Sle		    osblock.fs_bsize >= (int32_t) sizeof(struct fs))
146398542Smckusick			break;
146498542Smckusick	}
1465232548Strasz	if (sblock_try[i] == -1)
146669926Stomsoft		errx(1, "superblock not recognized");
146769800Stomsoft	memcpy((void *)&fsun1, (void *)&fsun2, sizeof(fsun2));
146869800Stomsoft
146969800Stomsoft	DBG_OPEN("/tmp/growfs.debug"); /* already here we need a superblock */
1470232548Strasz	DBG_DUMP_FS(&sblock, "old sblock");
147169800Stomsoft
147269800Stomsoft	/*
1473233656Strasz	 * Determine size to grow to. Default to the device size.
147469800Stomsoft	 */
1475234846Strasz	if (size == 0)
1476234846Strasz		size = mediasize;
1477234846Strasz	else {
1478234846Strasz		if (size > (uint64_t)mediasize) {
1479234846Strasz			humanize_number(oldsizebuf, sizeof(oldsizebuf), size,
1480234846Strasz			    "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
1481234846Strasz			humanize_number(newsizebuf, sizeof(newsizebuf),
1482234846Strasz			    mediasize,
1483234846Strasz			    "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
1484234846Strasz
1485234846Strasz			errx(1, "requested size %s is larger "
1486234846Strasz			    "than the available %s", oldsizebuf, newsizebuf);
1487234846Strasz		}
148869800Stomsoft	}
148969800Stomsoft
1490234846Strasz	if (size <= (uint64_t)(osblock.fs_size * osblock.fs_fsize)) {
1491234846Strasz		humanize_number(oldsizebuf, sizeof(oldsizebuf),
1492234846Strasz		    osblock.fs_size * osblock.fs_fsize,
1493234846Strasz		    "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
1494234846Strasz		humanize_number(newsizebuf, sizeof(newsizebuf), size,
1495234846Strasz		    "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
1496234846Strasz
1497234846Strasz		errx(1, "requested size %s is not larger than the current "
1498234846Strasz		   "filesystem size %s", newsizebuf, oldsizebuf);
1499234846Strasz	}
1500234846Strasz
1501234846Strasz	sblock.fs_size = dbtofsb(&osblock, size / DEV_BSIZE);
1502242379Strasz	sblock.fs_providersize = dbtofsb(&osblock, mediasize / DEV_BSIZE);
1503234846Strasz
150469800Stomsoft	/*
1505234846Strasz	 * Are we really growing?
150669800Stomsoft	 */
1507232548Strasz	if (osblock.fs_size >= sblock.fs_size) {
1508127798Sle		errx(1, "we are not growing (%jd->%jd)",
1509127798Sle		    (intmax_t)osblock.fs_size, (intmax_t)sblock.fs_size);
151069800Stomsoft	}
151169800Stomsoft
151269800Stomsoft	/*
151369800Stomsoft	 * Check if we find an active snapshot.
151469800Stomsoft	 */
1515234846Strasz	if (yflag == 0) {
1516232548Strasz		for (j = 0; j < FSMAXSNAP; j++) {
1517232548Strasz			if (sblock.fs_snapinum[j]) {
1518223652Strasz				errx(1, "active snapshot found in file system; "
1519223429Strasz				    "please remove all snapshots before "
1520140351Scharnier				    "using growfs");
152169800Stomsoft			}
1522232548Strasz			if (!sblock.fs_snapinum[j]) /* list is dense */
152369800Stomsoft				break;
152469800Stomsoft		}
152569800Stomsoft	}
152669800Stomsoft
1527234846Strasz	if (yflag == 0 && Nflag == 0) {
1528234846Strasz		if (statfsp != NULL && (statfsp->f_flags & MNT_RDONLY) == 0)
1529243246Strasz			printf("Device is mounted read-write; resizing will "
1530243246Strasz			    "result in temporary write suspension for %s.\n",
1531243246Strasz			    statfsp->f_mntonname);
1532234846Strasz		printf("It's strongly recommended to make a backup "
1533223652Strasz		    "before growing the file system.\n"
1534234846Strasz		    "OK to grow filesystem on %s", device);
1535234846Strasz		if (statfsp != NULL)
1536234846Strasz			printf(", mounted on %s,", statfsp->f_mntonname);
1537234846Strasz		humanize_number(oldsizebuf, sizeof(oldsizebuf),
1538234846Strasz		    osblock.fs_size * osblock.fs_fsize,
1539234846Strasz		    "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
1540234846Strasz		humanize_number(newsizebuf, sizeof(newsizebuf),
1541234846Strasz		    sblock.fs_size * sblock.fs_fsize,
1542234846Strasz		    "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
1543234846Strasz		printf(" from %s to %s? [Yes/No] ", oldsizebuf, newsizebuf);
1544234846Strasz		fflush(stdout);
154577885Stomsoft		fgets(reply, (int)sizeof(reply), stdin);
154669800Stomsoft		if (strcmp(reply, "Yes\n")){
1547223429Strasz			printf("\nNothing done\n");
154869800Stomsoft			exit (0);
1549114067Sschweikh		}
155069800Stomsoft	}
155169800Stomsoft
1552234846Strasz	/*
1553234846Strasz	 * Try to access our device for writing.  If it's not mounted,
1554234846Strasz	 * or mounted read-only, simply open it; otherwise, use UFS
1555234846Strasz	 * suspension mechanism.
1556234846Strasz	 */
1557234846Strasz	if (Nflag) {
1558234846Strasz		fso = -1;
1559234846Strasz	} else {
1560243246Strasz		if (statfsp != NULL && (statfsp->f_flags & MNT_RDONLY) == 0) {
1561243246Strasz			fso = open(_PATH_UFSSUSPEND, O_RDWR);
1562243246Strasz			if (fso == -1)
1563243246Strasz				err(1, "unable to open %s", _PATH_UFSSUSPEND);
1564243246Strasz			error = ioctl(fso, UFSSUSPEND, &statfsp->f_fsid);
1565243246Strasz			if (error != 0)
1566243246Strasz				err(1, "UFSSUSPEND");
1567243246Strasz		} else {
1568243246Strasz			fso = open(device, O_WRONLY);
1569243246Strasz			if (fso < 0)
1570243246Strasz				err(1, "%s", device);
1571243246Strasz		}
1572234846Strasz	}
157369800Stomsoft
157469800Stomsoft	/*
1575234846Strasz	 * Try to access our new last block in the file system.
157669800Stomsoft	 */
1577234846Strasz	testbuf = malloc(sblock.fs_fsize);
1578234846Strasz	if (testbuf == NULL)
1579234846Strasz		err(1, "malloc");
1580235079Strasz	rdfs((ufs2_daddr_t)((size - sblock.fs_fsize) / DEV_BSIZE),
1581234846Strasz	    sblock.fs_fsize, testbuf, fsi);
1582235079Strasz	wtfs((ufs2_daddr_t)((size - sblock.fs_fsize) / DEV_BSIZE),
1583234846Strasz	    sblock.fs_fsize, testbuf, fso, Nflag);
1584234846Strasz	free(testbuf);
158569800Stomsoft
158669800Stomsoft	/*
158769800Stomsoft	 * Now calculate new superblock values and check for reasonable
1588223652Strasz	 * bound for new file system size:
1589233656Strasz	 *     fs_size:    is derived from user input
159069800Stomsoft	 *     fs_dsize:   should get updated in the routines creating or
159169800Stomsoft	 *                 updating the cylinder groups on the fly
159269800Stomsoft	 *     fs_cstotal: should get updated in the routines creating or
159369800Stomsoft	 *                 updating the cylinder groups
159469800Stomsoft	 */
159569800Stomsoft
159669800Stomsoft	/*
1597223652Strasz	 * Update the number of cylinders and cylinder groups in the file system.
159869800Stomsoft	 */
159998542Smckusick	if (sblock.fs_magic == FS_UFS1_MAGIC) {
160098542Smckusick		sblock.fs_old_ncyl =
160198542Smckusick		    sblock.fs_size * sblock.fs_old_nspf / sblock.fs_old_spc;
160298542Smckusick		if (sblock.fs_size * sblock.fs_old_nspf >
160398542Smckusick		    sblock.fs_old_ncyl * sblock.fs_old_spc)
160498542Smckusick			sblock.fs_old_ncyl++;
160569800Stomsoft	}
160698542Smckusick	sblock.fs_ncg = howmany(sblock.fs_size, sblock.fs_fpg);
160769800Stomsoft
160898542Smckusick	if (sblock.fs_size % sblock.fs_fpg != 0 &&
160998542Smckusick	    sblock.fs_size % sblock.fs_fpg < cgdmin(&sblock, sblock.fs_ncg)) {
161069800Stomsoft		/*
161169800Stomsoft		 * The space in the new last cylinder group is too small,
161269800Stomsoft		 * so revert back.
161369800Stomsoft		 */
161469800Stomsoft		sblock.fs_ncg--;
161598542Smckusick		if (sblock.fs_magic == FS_UFS1_MAGIC)
161698542Smckusick			sblock.fs_old_ncyl = sblock.fs_ncg * sblock.fs_old_cpg;
1617127798Sle		printf("Warning: %jd sector(s) cannot be allocated.\n",
1618127798Sle		    (intmax_t)fsbtodb(&sblock, sblock.fs_size % sblock.fs_fpg));
161998542Smckusick		sblock.fs_size = sblock.fs_ncg * sblock.fs_fpg;
162069800Stomsoft	}
162169800Stomsoft
162269800Stomsoft	/*
162369800Stomsoft	 * Update the space for the cylinder group summary information in the
162469800Stomsoft	 * respective cylinder group data area.
162569800Stomsoft	 */
162669800Stomsoft	sblock.fs_cssize =
162769800Stomsoft	    fragroundup(&sblock, sblock.fs_ncg * sizeof(struct csum));
1628114067Sschweikh
1629232548Strasz	if (osblock.fs_size >= sblock.fs_size)
163069926Stomsoft		errx(1, "not enough new space");
163169800Stomsoft
163269800Stomsoft	DBG_PRINT0("sblock calculated\n");
163369800Stomsoft
163469800Stomsoft	/*
163569800Stomsoft	 * Ok, everything prepared, so now let's do the tricks.
163669800Stomsoft	 */
163769800Stomsoft	growfs(fsi, fso, Nflag);
163869800Stomsoft
163969800Stomsoft	close(fsi);
1640234846Strasz	if (fso > -1) {
1641243246Strasz		if (statfsp != NULL && (statfsp->f_flags & MNT_RDONLY) == 0) {
1642243246Strasz			error = ioctl(fso, UFSRESUME);
1643243246Strasz			if (error != 0)
1644243246Strasz				err(1, "UFSRESUME");
1645243246Strasz		}
1646234846Strasz		error = close(fso);
1647234846Strasz		if (error != 0)
1648234846Strasz			err(1, "close");
1649243246Strasz		if (statfsp != NULL && (statfsp->f_flags & MNT_RDONLY) != 0)
1650243246Strasz			mount_reload(statfsp);
1651234846Strasz	}
165269800Stomsoft
165369800Stomsoft	DBG_CLOSE;
165469800Stomsoft
165569800Stomsoft	DBG_LEAVE;
1656234420Strasz	return (0);
165769800Stomsoft}
165869800Stomsoft
165969800Stomsoft/*
166069800Stomsoft * Dump a line of usage.
166169800Stomsoft */
166269800Stomsoftstatic void
166369926Stomsoftusage(void)
1664114067Sschweikh{
166569800Stomsoft	DBG_FUNC("usage")
166669800Stomsoft
166769800Stomsoft	DBG_ENTER;
166869800Stomsoft
1669234846Strasz	fprintf(stderr, "usage: growfs [-Ny] [-s size] special | filesystem\n");
167069926Stomsoft
167169800Stomsoft	DBG_LEAVE;
167269926Stomsoft	exit(1);
167369800Stomsoft}
167469800Stomsoft
167569800Stomsoft/*
1676114067Sschweikh * This updates most parameters and the bitmap related to cluster. We have to
1677114067Sschweikh * assume that sblock, osblock, acg are set up.
167869800Stomsoft */
167969800Stomsoftstatic void
168069800Stomsoftupdclst(int block)
1681114067Sschweikh{
168269800Stomsoft	DBG_FUNC("updclst")
1683232548Strasz	static int lcs = 0;
168469800Stomsoft
168569800Stomsoft	DBG_ENTER;
168669800Stomsoft
1687232548Strasz	if (sblock.fs_contigsumsize < 1) /* no clustering */
168869800Stomsoft		return;
168969800Stomsoft	/*
169069800Stomsoft	 * update cluster allocation map
169169800Stomsoft	 */
169269800Stomsoft	setbit(cg_clustersfree(&acg), block);
169369800Stomsoft
169469800Stomsoft	/*
169569800Stomsoft	 * update cluster summary table
169669800Stomsoft	 */
1697232548Strasz	if (!lcs) {
169869800Stomsoft		/*
169969800Stomsoft		 * calculate size for the trailing cluster
170069800Stomsoft		 */
1701232548Strasz		for (block--; lcs < sblock.fs_contigsumsize; block--, lcs++ ) {
1702232548Strasz			if (isclr(cg_clustersfree(&acg), block))
170369800Stomsoft				break;
170469800Stomsoft		}
1705114067Sschweikh	}
1706232548Strasz	if (lcs < sblock.fs_contigsumsize) {
1707232548Strasz		if (lcs)
170869800Stomsoft			cg_clustersum(&acg)[lcs]--;
170969800Stomsoft		lcs++;
171069800Stomsoft		cg_clustersum(&acg)[lcs]++;
171169800Stomsoft	}
171269800Stomsoft
171369800Stomsoft	DBG_LEAVE;
171469800Stomsoft	return;
171569800Stomsoft}
1716234846Strasz
1717234846Straszstatic void
1718234846Straszmount_reload(const struct statfs *stfs)
1719234846Strasz{
1720234846Strasz	char errmsg[255];
1721234846Strasz	struct iovec *iov;
1722234846Strasz	int iovlen;
1723234846Strasz
1724234846Strasz	iov = NULL;
1725234846Strasz	iovlen = 0;
1726234846Strasz	*errmsg = '\0';
1727234846Strasz	build_iovec(&iov, &iovlen, "fstype", __DECONST(char *, "ffs"), 4);
1728234846Strasz	build_iovec(&iov, &iovlen, "fspath", __DECONST(char *, stfs->f_mntonname), (size_t)-1);
1729234846Strasz	build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
1730234846Strasz	build_iovec(&iov, &iovlen, "update", NULL, 0);
1731234846Strasz	build_iovec(&iov, &iovlen, "reload", NULL, 0);
1732234846Strasz
1733234846Strasz	if (nmount(iov, iovlen, stfs->f_flags) < 0) {
1734234846Strasz		errmsg[sizeof(errmsg) - 1] = '\0';
1735234846Strasz		err(9, "%s: cannot reload filesystem%s%s", stfs->f_mntonname,
1736234846Strasz		    *errmsg != '\0' ? ": " : "", errmsg);
1737234846Strasz	}
1738234846Strasz}
1739