mkfs.c revision 92711
1/*
2 * Copyright (c) 1980, 1989, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35#if 0
36static char sccsid[] = "@(#)mkfs.c	8.11 (Berkeley) 5/3/95";
37#endif
38static const char rcsid[] =
39  "$FreeBSD: head/sbin/newfs/mkfs.c 92711 2002-03-19 17:20:02Z iedowse $";
40#endif /* not lint */
41
42#include <err.h>
43#include <signal.h>
44#include <stdlib.h>
45#include <string.h>
46#include <stdio.h>
47#include <unistd.h>
48#include <sys/param.h>
49#include <sys/time.h>
50#include <sys/types.h>
51#include <sys/wait.h>
52#include <sys/resource.h>
53#include <sys/stat.h>
54#include <ufs/ufs/dinode.h>
55#include <ufs/ufs/dir.h>
56#include <ufs/ffs/fs.h>
57#include <sys/disklabel.h>
58#include <sys/file.h>
59#include <sys/mman.h>
60#include <sys/ioctl.h>
61
62/*
63 * make file system for cylinder-group style file systems
64 */
65
66/*
67 * We limit the size of the inode map to be no more than a
68 * third of the cylinder group space, since we must leave at
69 * least an equal amount of space for the block map.
70 *
71 * N.B.: MAXIPG must be a multiple of INOPB(fs).
72 */
73#define MAXIPG(fs)	roundup((fs)->fs_bsize * NBBY / 3, INOPB(fs))
74
75#define UMASK		0755
76#define MAXINOPB	(MAXBSIZE / sizeof(struct dinode))
77#define POWEROF2(num)	(((num) & ((num) - 1)) == 0)
78
79/*
80 * variables set up by front end.
81 */
82extern int	Nflag;		/* run mkfs without writing file system */
83extern int	Oflag;		/* format as an 4.3BSD file system */
84extern int	Uflag;		/* enable soft updates for file system */
85extern int	fssize;		/* file system size */
86extern int	ntracks;	/* # tracks/cylinder */
87extern int	nsectors;	/* # sectors/track */
88extern int	nphyssectors;	/* # sectors/track including spares */
89extern int	secpercyl;	/* sectors per cylinder */
90extern int	sectorsize;	/* bytes/sector */
91extern int	realsectorsize;	/* bytes/sector in hardware*/
92extern int	rpm;		/* revolutions/minute of drive */
93extern int	interleave;	/* hardware sector interleave */
94extern int	trackskew;	/* sector 0 skew, per track */
95extern int	fsize;		/* fragment size */
96extern int	bsize;		/* block size */
97extern int	cpg;		/* cylinders/cylinder group */
98extern int	cpgflg;		/* cylinders/cylinder group flag was given */
99extern int	minfree;	/* free space threshold */
100extern int	opt;		/* optimization preference (space or time) */
101extern int	density;	/* number of bytes per inode */
102extern int	maxcontig;	/* max contiguous blocks to allocate */
103extern int	rotdelay;	/* rotational delay between blocks */
104extern int	maxbpg;		/* maximum blocks per file in a cyl group */
105extern int	nrpos;		/* # of distinguished rotational positions */
106extern int	bbsize;		/* boot block size */
107extern int	sbsize;		/* superblock size */
108extern int	avgfilesize;	/* expected average file size */
109extern int	avgfilesperdir;	/* expected number of files per directory */
110
111union {
112	struct fs fs;
113	char pad[SBSIZE];
114} fsun;
115#define	sblock	fsun.fs
116struct	csum *fscs;
117
118union {
119	struct cg cg;
120	char pad[MAXBSIZE];
121} cgun;
122#define	acg	cgun.cg
123
124struct dinode zino[MAXBSIZE / sizeof(struct dinode)];
125
126int	fsi, fso;
127int     randinit;
128daddr_t	alloc();
129long	calcipg();
130static int charsperline();
131void clrblock (struct fs *, unsigned char *, int);
132void fsinit (time_t);
133void initcg (int, time_t);
134int isblock (struct fs *, unsigned char *, int);
135void iput (struct dinode *, ino_t);
136int makedir (struct direct *, int);
137void rdfs (daddr_t, int, char *);
138void setblock (struct fs *, unsigned char *, int);
139void wtfs (daddr_t, int, char *);
140void wtfsflush (void);
141
142void
143mkfs(struct partition *pp, char *fsys, int fi, int fo)
144{
145	long i, mincpc, mincpg, inospercg;
146	long cylno, rpos, blk, j, warn = 0;
147	long used, mincpgcnt, bpcg;
148	off_t usedb;
149	long mapcramped, inodecramped;
150	long postblsize, rotblsize, totalsbsize;
151	time_t utime;
152	quad_t sizepb;
153	int width;
154	char tmpbuf[100];	/* XXX this will break in about 2,500 years */
155
156	time(&utime);
157	if (!randinit) {
158		randinit = 1;
159		srandomdev();
160	}
161	fsi = fi;
162	fso = fo;
163	if (Oflag) {
164		sblock.fs_inodefmt = FS_42INODEFMT;
165		sblock.fs_maxsymlinklen = 0;
166	} else {
167		sblock.fs_inodefmt = FS_44INODEFMT;
168		sblock.fs_maxsymlinklen = MAXSYMLINKLEN;
169	}
170	if (Uflag)
171		sblock.fs_flags |= FS_DOSOFTDEP;
172	/*
173	 * Validate the given file system size.
174	 * Verify that its last block can actually be accessed.
175	 */
176	if (fssize <= 0)
177		printf("preposterous size %d\n", fssize), exit(13);
178	wtfs(fssize - (realsectorsize / DEV_BSIZE), realsectorsize,
179	    (char *)&sblock);
180	/*
181	 * collect and verify the sector and track info
182	 */
183	sblock.fs_nsect = nsectors;
184	sblock.fs_ntrak = ntracks;
185	if (sblock.fs_ntrak <= 0)
186		printf("preposterous ntrak %d\n", sblock.fs_ntrak), exit(14);
187	if (sblock.fs_nsect <= 0)
188		printf("preposterous nsect %d\n", sblock.fs_nsect), exit(15);
189	/*
190	 * collect and verify the filesystem density info
191	 */
192	sblock.fs_avgfilesize = avgfilesize;
193	sblock.fs_avgfpdir = avgfilesperdir;
194	if (sblock.fs_avgfilesize <= 0)
195		printf("illegal expected average file size %d\n",
196		    sblock.fs_avgfilesize), exit(14);
197	if (sblock.fs_avgfpdir <= 0)
198		printf("illegal expected number of files per directory %d\n",
199		    sblock.fs_avgfpdir), exit(15);
200	/*
201	 * collect and verify the block and fragment sizes
202	 */
203	sblock.fs_bsize = bsize;
204	sblock.fs_fsize = fsize;
205	if (!POWEROF2(sblock.fs_bsize)) {
206		printf("block size must be a power of 2, not %d\n",
207		    sblock.fs_bsize);
208		exit(16);
209	}
210	if (!POWEROF2(sblock.fs_fsize)) {
211		printf("fragment size must be a power of 2, not %d\n",
212		    sblock.fs_fsize);
213		exit(17);
214	}
215	if (sblock.fs_fsize < sectorsize) {
216		printf("fragment size %d is too small, minimum is %d\n",
217		    sblock.fs_fsize, sectorsize);
218		exit(18);
219	}
220	if (sblock.fs_bsize < MINBSIZE) {
221		printf("block size %d is too small, minimum is %d\n",
222		    sblock.fs_bsize, MINBSIZE);
223		exit(19);
224	}
225	if (sblock.fs_bsize < sblock.fs_fsize) {
226		printf(
227		"block size (%d) cannot be smaller than fragment size (%d)\n",
228		    sblock.fs_bsize, sblock.fs_fsize);
229		exit(20);
230	}
231	sblock.fs_bmask = ~(sblock.fs_bsize - 1);
232	sblock.fs_fmask = ~(sblock.fs_fsize - 1);
233	sblock.fs_qbmask = ~sblock.fs_bmask;
234	sblock.fs_qfmask = ~sblock.fs_fmask;
235	for (sblock.fs_bshift = 0, i = sblock.fs_bsize; i > 1; i >>= 1)
236		sblock.fs_bshift++;
237	for (sblock.fs_fshift = 0, i = sblock.fs_fsize; i > 1; i >>= 1)
238		sblock.fs_fshift++;
239	sblock.fs_frag = numfrags(&sblock, sblock.fs_bsize);
240	for (sblock.fs_fragshift = 0, i = sblock.fs_frag; i > 1; i >>= 1)
241		sblock.fs_fragshift++;
242	if (sblock.fs_frag > MAXFRAG) {
243		printf(
244	"fragment size %d is too small, minimum with block size %d is %d\n",
245		    sblock.fs_fsize, sblock.fs_bsize,
246		    sblock.fs_bsize / MAXFRAG);
247		exit(21);
248	}
249	sblock.fs_nrpos = nrpos;
250	sblock.fs_nindir = sblock.fs_bsize / sizeof(daddr_t);
251	sblock.fs_inopb = sblock.fs_bsize / sizeof(struct dinode);
252	sblock.fs_nspf = sblock.fs_fsize / sectorsize;
253	for (sblock.fs_fsbtodb = 0, i = NSPF(&sblock); i > 1; i >>= 1)
254		sblock.fs_fsbtodb++;
255	sblock.fs_sblkno =
256	    roundup(howmany(bbsize + sbsize, sblock.fs_fsize), sblock.fs_frag);
257	sblock.fs_cblkno = (daddr_t)(sblock.fs_sblkno +
258	    roundup(howmany(sbsize, sblock.fs_fsize), sblock.fs_frag));
259	sblock.fs_iblkno = sblock.fs_cblkno + sblock.fs_frag;
260	sblock.fs_cgoffset =
261	    roundup(howmany(sblock.fs_nsect, NSPF(&sblock)), sblock.fs_frag);
262	for (sblock.fs_cgmask = 0xffffffff, i = sblock.fs_ntrak; i > 1; i >>= 1)
263		sblock.fs_cgmask <<= 1;
264	if (!POWEROF2(sblock.fs_ntrak))
265		sblock.fs_cgmask <<= 1;
266	sblock.fs_maxfilesize = sblock.fs_bsize * NDADDR - 1;
267	for (sizepb = sblock.fs_bsize, i = 0; i < NIADDR; i++) {
268		sizepb *= NINDIR(&sblock);
269		sblock.fs_maxfilesize += sizepb;
270	}
271	/*
272	 * Validate specified/determined secpercyl
273	 * and calculate minimum cylinders per group.
274	 */
275	sblock.fs_spc = secpercyl;
276	for (sblock.fs_cpc = NSPB(&sblock), i = sblock.fs_spc;
277	     sblock.fs_cpc > 1 && (i & 1) == 0;
278	     sblock.fs_cpc >>= 1, i >>= 1)
279		/* void */;
280	mincpc = sblock.fs_cpc;
281	bpcg = sblock.fs_spc * sectorsize;
282	inospercg = roundup(bpcg / sizeof(struct dinode), INOPB(&sblock));
283	if (inospercg > MAXIPG(&sblock))
284		inospercg = MAXIPG(&sblock);
285	used = (sblock.fs_iblkno + inospercg / INOPF(&sblock)) * NSPF(&sblock);
286	mincpgcnt = howmany(sblock.fs_cgoffset * (~sblock.fs_cgmask) + used,
287	    sblock.fs_spc);
288	mincpg = roundup(mincpgcnt, mincpc);
289	/*
290	 * Ensure that cylinder group with mincpg has enough space
291	 * for block maps.
292	 */
293	sblock.fs_cpg = mincpg;
294	sblock.fs_ipg = inospercg;
295	if (maxcontig > 1)
296		sblock.fs_contigsumsize = MIN(maxcontig, FS_MAXCONTIG);
297	mapcramped = 0;
298	while (CGSIZE(&sblock) > sblock.fs_bsize) {
299		mapcramped = 1;
300		if (sblock.fs_bsize < MAXBSIZE) {
301			sblock.fs_bsize <<= 1;
302			if ((i & 1) == 0)
303				i >>= 1;
304			else {
305				sblock.fs_cpc <<= 1;
306				mincpc <<= 1;
307				mincpg = roundup(mincpgcnt, mincpc);
308				sblock.fs_cpg = mincpg;
309			}
310			sblock.fs_frag <<= 1;
311			sblock.fs_fragshift += 1;
312			if (sblock.fs_frag <= MAXFRAG)
313				continue;
314		}
315		if (sblock.fs_fsize == sblock.fs_bsize) {
316			printf("There is no block size that");
317			printf(" can support this disk\n");
318			exit(22);
319		}
320		sblock.fs_frag >>= 1;
321		sblock.fs_fragshift -= 1;
322		sblock.fs_fsize <<= 1;
323		sblock.fs_nspf <<= 1;
324	}
325	/*
326	 * Ensure that cylinder group with mincpg has enough space for inodes.
327	 */
328	inodecramped = 0;
329	inospercg = calcipg(mincpg, bpcg, &usedb);
330	sblock.fs_ipg = inospercg;
331	while (inospercg > MAXIPG(&sblock)) {
332		inodecramped = 1;
333		if (mincpc == 1 || sblock.fs_frag == 1 ||
334		    sblock.fs_bsize == MINBSIZE)
335			break;
336		printf("With a block size of %d %s %d\n", sblock.fs_bsize,
337		    "minimum bytes per inode is",
338		    (int)((mincpg * (off_t)bpcg - usedb) /
339		    MAXIPG(&sblock) + 1));
340		sblock.fs_bsize >>= 1;
341		sblock.fs_frag >>= 1;
342		sblock.fs_fragshift -= 1;
343		mincpc >>= 1;
344		sblock.fs_cpg = roundup(mincpgcnt, mincpc);
345		if (CGSIZE(&sblock) > sblock.fs_bsize) {
346			sblock.fs_bsize <<= 1;
347			break;
348		}
349		mincpg = sblock.fs_cpg;
350		inospercg = calcipg(mincpg, bpcg, &usedb);
351		sblock.fs_ipg = inospercg;
352	}
353	if (inodecramped) {
354		if (inospercg > MAXIPG(&sblock)) {
355			printf("Minimum bytes per inode is %d\n",
356			    (int)((mincpg * (off_t)bpcg - usedb) /
357			    MAXIPG(&sblock) + 1));
358		} else if (!mapcramped) {
359			printf("With %d bytes per inode, ", density);
360			printf("minimum cylinders per group is %ld\n", mincpg);
361		}
362	}
363	if (mapcramped) {
364		printf("With %d sectors per cylinder, ", sblock.fs_spc);
365		printf("minimum cylinders per group is %ld\n", mincpg);
366	}
367	if (inodecramped || mapcramped) {
368		if (sblock.fs_bsize != bsize)
369			printf("%s to be changed from %d to %d\n",
370			    "This requires the block size",
371			    bsize, sblock.fs_bsize);
372		if (sblock.fs_fsize != fsize)
373			printf("\t%s to be changed from %d to %d\n",
374			    "and the fragment size", fsize, sblock.fs_fsize);
375		exit(23);
376	}
377	/*
378	 * Calculate the number of cylinders per group
379	 */
380	sblock.fs_cpg = cpg;
381	if (sblock.fs_cpg % mincpc != 0) {
382		printf("%s groups must have a multiple of %ld cylinders\n",
383		    cpgflg ? "Cylinder" : "Warning: cylinder", mincpc);
384		sblock.fs_cpg = roundup(sblock.fs_cpg, mincpc);
385		if (!cpgflg)
386			cpg = sblock.fs_cpg;
387	}
388	/*
389	 * Must ensure there is enough space for inodes.
390	 */
391	sblock.fs_ipg = calcipg(sblock.fs_cpg, bpcg, &usedb);
392	while (sblock.fs_ipg > MAXIPG(&sblock)) {
393		inodecramped = 1;
394		sblock.fs_cpg -= mincpc;
395		sblock.fs_ipg = calcipg(sblock.fs_cpg, bpcg, &usedb);
396	}
397	/*
398	 * Must ensure there is enough space to hold block map.
399	 */
400	while (CGSIZE(&sblock) > sblock.fs_bsize) {
401		mapcramped = 1;
402		sblock.fs_cpg -= mincpc;
403		sblock.fs_ipg = calcipg(sblock.fs_cpg, bpcg, &usedb);
404	}
405	sblock.fs_fpg = (sblock.fs_cpg * sblock.fs_spc) / NSPF(&sblock);
406	if ((sblock.fs_cpg * sblock.fs_spc) % NSPB(&sblock) != 0) {
407		printf("panic (fs_cpg * fs_spc) %% NSPF != 0");
408		exit(24);
409	}
410	if (sblock.fs_cpg < mincpg) {
411		printf("cylinder groups must have at least %ld cylinders\n",
412			mincpg);
413		exit(25);
414	} else if (sblock.fs_cpg != cpg) {
415		if (!cpgflg)
416			printf("Warning: ");
417		else if (!mapcramped && !inodecramped)
418			exit(26);
419		if (mapcramped && inodecramped)
420			printf("Block size and bytes per inode restrict");
421		else if (mapcramped)
422			printf("Block size restricts");
423		else
424			printf("Bytes per inode restrict");
425		printf(" cylinders per group to %d.\n", sblock.fs_cpg);
426		if (cpgflg)
427			exit(27);
428	}
429	sblock.fs_cgsize = fragroundup(&sblock, CGSIZE(&sblock));
430	/*
431	 * Now have size for file system and nsect and ntrak.
432	 * Determine number of cylinders and blocks in the file system.
433	 */
434	sblock.fs_size = fssize = dbtofsb(&sblock, fssize);
435	sblock.fs_ncyl = fssize * NSPF(&sblock) / sblock.fs_spc;
436	if (fssize * NSPF(&sblock) > sblock.fs_ncyl * sblock.fs_spc) {
437		sblock.fs_ncyl++;
438		warn = 1;
439	}
440	if (sblock.fs_ncyl < 1) {
441		printf("file systems must have at least one cylinder\n");
442		exit(28);
443	}
444	/*
445	 * Determine feasability/values of rotational layout tables.
446	 *
447	 * The size of the rotational layout tables is limited by the
448	 * size of the superblock, SBSIZE. The amount of space available
449	 * for tables is calculated as (SBSIZE - sizeof (struct fs)).
450	 * The size of these tables is inversely proportional to the block
451	 * size of the file system. The size increases if sectors per track
452	 * are not powers of two, because more cylinders must be described
453	 * by the tables before the rotational pattern repeats (fs_cpc).
454	 */
455	sblock.fs_interleave = interleave;
456	sblock.fs_trackskew = trackskew;
457	sblock.fs_npsect = nphyssectors;
458	sblock.fs_postblformat = FS_DYNAMICPOSTBLFMT;
459	sblock.fs_sbsize = fragroundup(&sblock, sizeof(struct fs));
460	if (sblock.fs_sbsize > SBSIZE)
461		sblock.fs_sbsize = SBSIZE;
462	if (sblock.fs_ntrak == 1) {
463		sblock.fs_cpc = 0;
464		goto next;
465	}
466	postblsize = sblock.fs_nrpos * sblock.fs_cpc * sizeof(int16_t);
467	rotblsize = sblock.fs_cpc * sblock.fs_spc / NSPB(&sblock);
468	totalsbsize = sizeof(struct fs) + rotblsize;
469	if (sblock.fs_nrpos == 8 && sblock.fs_cpc <= 16) {
470		/* use old static table space */
471		sblock.fs_postbloff = (char *)(&sblock.fs_opostbl[0][0]) -
472		    (char *)(&sblock.fs_firstfield);
473		sblock.fs_rotbloff = &sblock.fs_space[0] -
474		    (u_char *)(&sblock.fs_firstfield);
475	} else {
476		/* use dynamic table space */
477		sblock.fs_postbloff = &sblock.fs_space[0] -
478		    (u_char *)(&sblock.fs_firstfield);
479		sblock.fs_rotbloff = sblock.fs_postbloff + postblsize;
480		totalsbsize += postblsize;
481	}
482	if (totalsbsize > SBSIZE ||
483	    sblock.fs_nsect > (1 << NBBY) * NSPB(&sblock)) {
484		printf("%s %s %d %s %d.%s",
485		    "Warning: insufficient space in super block for\n",
486		    "rotational layout tables with nsect", sblock.fs_nsect,
487		    "and ntrak", sblock.fs_ntrak,
488		    "\nFile system performance may be impaired.\n");
489		sblock.fs_cpc = 0;
490		goto next;
491	}
492	sblock.fs_sbsize = fragroundup(&sblock, totalsbsize);
493	if (sblock.fs_sbsize > SBSIZE)
494		sblock.fs_sbsize = SBSIZE;
495	/*
496	 * calculate the available blocks for each rotational position
497	 */
498	for (cylno = 0; cylno < sblock.fs_cpc; cylno++)
499		for (rpos = 0; rpos < sblock.fs_nrpos; rpos++)
500			fs_postbl(&sblock, cylno)[rpos] = -1;
501	for (i = (rotblsize - 1) * sblock.fs_frag;
502	     i >= 0; i -= sblock.fs_frag) {
503		cylno = cbtocylno(&sblock, i);
504		rpos = cbtorpos(&sblock, i);
505		blk = fragstoblks(&sblock, i);
506		if (fs_postbl(&sblock, cylno)[rpos] == -1)
507			fs_rotbl(&sblock)[blk] = 0;
508		else
509			fs_rotbl(&sblock)[blk] =
510			    fs_postbl(&sblock, cylno)[rpos] - blk;
511		fs_postbl(&sblock, cylno)[rpos] = blk;
512	}
513next:
514	/*
515	 * Compute/validate number of cylinder groups.
516	 */
517	sblock.fs_ncg = sblock.fs_ncyl / sblock.fs_cpg;
518	if (sblock.fs_ncyl % sblock.fs_cpg)
519		sblock.fs_ncg++;
520	sblock.fs_dblkno = sblock.fs_iblkno + sblock.fs_ipg / INOPF(&sblock);
521	i = MIN(~sblock.fs_cgmask, sblock.fs_ncg - 1);
522	if (cgdmin(&sblock, i) - cgbase(&sblock, i) >= sblock.fs_fpg) {
523		printf("inode blocks/cyl group (%ld) >= data blocks (%ld)\n",
524		    cgdmin(&sblock, i) - cgbase(&sblock, i) / sblock.fs_frag,
525		    (long)(sblock.fs_fpg / sblock.fs_frag));
526		printf("number of cylinders per cylinder group (%d) %s.\n",
527		    sblock.fs_cpg, "must be increased");
528		exit(29);
529	}
530	j = sblock.fs_ncg - 1;
531	if ((i = fssize - j * sblock.fs_fpg) < sblock.fs_fpg &&
532	    cgdmin(&sblock, j) - cgbase(&sblock, j) > i) {
533		if (j == 0) {
534			printf("Filesystem must have at least %d sectors\n",
535			    NSPF(&sblock) *
536			    (cgdmin(&sblock, 0) + 3 * sblock.fs_frag));
537			exit(30);
538		}
539		printf(
540"Warning: inode blocks/cyl group (%ld) >= data blocks (%ld) in last\n",
541		    (cgdmin(&sblock, j) - cgbase(&sblock, j)) / sblock.fs_frag,
542		    i / sblock.fs_frag);
543		printf(
544"    cylinder group. This implies %ld sector(s) cannot be allocated.\n",
545		    i * NSPF(&sblock));
546		sblock.fs_ncg--;
547		sblock.fs_ncyl -= sblock.fs_ncyl % sblock.fs_cpg;
548		sblock.fs_size = fssize = sblock.fs_ncyl * sblock.fs_spc /
549		    NSPF(&sblock);
550		warn = 0;
551	}
552	if (warn) {
553		printf("Warning: %d sector(s) in last cylinder unallocated\n",
554		    sblock.fs_spc -
555		    (fssize * NSPF(&sblock) - (sblock.fs_ncyl - 1) *
556		    sblock.fs_spc));
557	}
558	/*
559	 * fill in remaining fields of the super block
560	 */
561	sblock.fs_csaddr = cgdmin(&sblock, 0);
562	sblock.fs_cssize =
563	    fragroundup(&sblock, sblock.fs_ncg * sizeof(struct csum));
564	/*
565	 * The superblock fields 'fs_csmask' and 'fs_csshift' are no
566	 * longer used. However, we still initialise them so that the
567	 * filesystem remains compatible with old kernels.
568	 */
569	i = sblock.fs_bsize / sizeof(struct csum);
570	sblock.fs_csmask = ~(i - 1);
571	for (sblock.fs_csshift = 0; i > 1; i >>= 1)
572		sblock.fs_csshift++;
573	fscs = (struct csum *)calloc(1, sblock.fs_cssize);
574	if (fscs == NULL)
575		errx(31, "calloc failed");
576	sblock.fs_magic = FS_MAGIC;
577	sblock.fs_rotdelay = rotdelay;
578	sblock.fs_minfree = minfree;
579	sblock.fs_maxcontig = maxcontig;
580	sblock.fs_maxbpg = maxbpg;
581	sblock.fs_rps = rpm / 60;
582	sblock.fs_optim = opt;
583	sblock.fs_cgrotor = 0;
584	sblock.fs_cstotal.cs_ndir = 0;
585	sblock.fs_cstotal.cs_nbfree = 0;
586	sblock.fs_cstotal.cs_nifree = 0;
587	sblock.fs_cstotal.cs_nffree = 0;
588	sblock.fs_fmod = 0;
589	sblock.fs_ronly = 0;
590	sblock.fs_clean = 1;
591	sblock.fs_id[0] = (long)utime;
592	sblock.fs_id[1] = random();
593
594	/*
595	 * Dump out summary information about file system.
596	 */
597	printf("%s:\t%d sectors in %d %s of %d tracks, %d sectors\n",
598	    fsys, sblock.fs_size * NSPF(&sblock), sblock.fs_ncyl,
599	    "cylinders", sblock.fs_ntrak, sblock.fs_nsect);
600#define B2MBFACTOR (1 / (1024.0 * 1024.0))
601	printf("\t%.1fMB in %d cyl groups (%d c/g, %.2fMB/g, %d i/g)%s\n",
602	    (float)sblock.fs_size * sblock.fs_fsize * B2MBFACTOR,
603	    sblock.fs_ncg, sblock.fs_cpg,
604	    (float)sblock.fs_fpg * sblock.fs_fsize * B2MBFACTOR,
605	    sblock.fs_ipg,
606	    sblock.fs_flags & FS_DOSOFTDEP ? " SOFTUPDATES" : "");
607#undef B2MBFACTOR
608	/*
609	 * Now build the cylinders group blocks and
610	 * then print out indices of cylinder groups.
611	 */
612	printf("super-block backups (for fsck -b #) at:\n");
613	i = 0;
614	width = charsperline();
615	for (cylno = 0; cylno < sblock.fs_ncg; cylno++) {
616		initcg(cylno, utime);
617		j = snprintf(tmpbuf, sizeof(tmpbuf), " %ld%s",
618		    fsbtodb(&sblock, cgsblock(&sblock, cylno)),
619		    cylno < (sblock.fs_ncg-1) ? "," : "");
620		if (j < 0)
621			tmpbuf[j = 0] = '\0';
622		if (i + j >= width) {
623			printf("\n");
624			i = 0;
625		}
626		i += j;
627		printf("%s", tmpbuf);
628		fflush(stdout);
629	}
630	printf("\n");
631	if (Nflag)
632		exit(0);
633	/*
634	 * Now construct the initial file system,
635	 * then write out the super-block.
636	 */
637	fsinit(utime);
638	sblock.fs_time = utime;
639	wtfs((int)SBOFF / sectorsize, sbsize, (char *)&sblock);
640	for (i = 0; i < sblock.fs_cssize; i += sblock.fs_bsize)
641		wtfs(fsbtodb(&sblock, sblock.fs_csaddr + numfrags(&sblock, i)),
642			sblock.fs_cssize - i < sblock.fs_bsize ?
643			sblock.fs_cssize - i : sblock.fs_bsize,
644			((char *)fscs) + i);
645	/*
646	 * Write out the duplicate super blocks
647	 */
648	for (cylno = 0; cylno < sblock.fs_ncg; cylno++)
649		wtfs(fsbtodb(&sblock, cgsblock(&sblock, cylno)),
650		    sbsize, (char *)&sblock);
651	wtfsflush();
652	/*
653	 * Update information about this partion in pack
654	 * label, to that it may be updated on disk.
655	 */
656	pp->p_fstype = FS_BSDFFS;
657	pp->p_fsize = sblock.fs_fsize;
658	pp->p_frag = sblock.fs_frag;
659	pp->p_cpg = sblock.fs_cpg;
660}
661
662/*
663 * Initialize a cylinder group.
664 */
665void
666initcg(int cylno, time_t utime)
667{
668	daddr_t cbase, d, dlower, dupper, dmax, blkno;
669	struct csum *cs;
670	long i, j;
671
672	/*
673	 * Determine block bounds for cylinder group.
674	 * Allow space for super block summary information in first
675	 * cylinder group.
676	 */
677	cbase = cgbase(&sblock, cylno);
678	dmax = cbase + sblock.fs_fpg;
679	if (dmax > sblock.fs_size)
680		dmax = sblock.fs_size;
681	dlower = cgsblock(&sblock, cylno) - cbase;
682	dupper = cgdmin(&sblock, cylno) - cbase;
683	if (cylno == 0)
684		dupper += howmany(sblock.fs_cssize, sblock.fs_fsize);
685	cs = fscs + cylno;
686	memset(&acg, 0, sblock.fs_cgsize);
687	acg.cg_time = utime;
688	acg.cg_magic = CG_MAGIC;
689	acg.cg_cgx = cylno;
690	if (cylno == sblock.fs_ncg - 1)
691		acg.cg_ncyl = sblock.fs_ncyl % sblock.fs_cpg;
692	else
693		acg.cg_ncyl = sblock.fs_cpg;
694	acg.cg_niblk = sblock.fs_ipg;
695	acg.cg_ndblk = dmax - cbase;
696	if (sblock.fs_contigsumsize > 0)
697		acg.cg_nclusterblks = acg.cg_ndblk / sblock.fs_frag;
698	acg.cg_btotoff = &acg.cg_space[0] - (u_char *)(&acg.cg_firstfield);
699	acg.cg_boff = acg.cg_btotoff + sblock.fs_cpg * sizeof(int32_t);
700	acg.cg_iusedoff = acg.cg_boff +
701		sblock.fs_cpg * sblock.fs_nrpos * sizeof(u_int16_t);
702	acg.cg_freeoff = acg.cg_iusedoff + howmany(sblock.fs_ipg, NBBY);
703	if (sblock.fs_contigsumsize <= 0) {
704		acg.cg_nextfreeoff = acg.cg_freeoff +
705		    howmany(sblock.fs_cpg * sblock.fs_spc / NSPF(&sblock),
706		    NBBY);
707	} else {
708		acg.cg_clustersumoff = acg.cg_freeoff + howmany
709		    (sblock.fs_cpg * sblock.fs_spc / NSPF(&sblock), NBBY) -
710		    sizeof(u_int32_t);
711		acg.cg_clustersumoff =
712		    roundup(acg.cg_clustersumoff, sizeof(u_int32_t));
713		acg.cg_clusteroff = acg.cg_clustersumoff +
714		    (sblock.fs_contigsumsize + 1) * sizeof(u_int32_t);
715		acg.cg_nextfreeoff = acg.cg_clusteroff + howmany
716		    (sblock.fs_cpg * sblock.fs_spc / NSPB(&sblock), NBBY);
717	}
718	if (acg.cg_nextfreeoff - (long)(&acg.cg_firstfield) >
719	    sblock.fs_cgsize) {
720		printf("Panic: cylinder group too big\n");
721		exit(37);
722	}
723	acg.cg_cs.cs_nifree += sblock.fs_ipg;
724	if (cylno == 0)
725		for (i = 0; i < ROOTINO; i++) {
726			setbit(cg_inosused(&acg), i);
727			acg.cg_cs.cs_nifree--;
728		}
729	for (i = 0; i < sblock.fs_ipg / INOPF(&sblock); i += sblock.fs_frag) {
730		for (j = 0; j < sblock.fs_bsize / sizeof(struct dinode); j++)
731			zino[j].di_gen = random();
732		wtfs(fsbtodb(&sblock, cgimin(&sblock, cylno) + i),
733		    sblock.fs_bsize, (char *)zino);
734	}
735	if (cylno > 0) {
736		/*
737		 * In cylno 0, beginning space is reserved
738		 * for boot and super blocks.
739		 */
740		for (d = 0; d < dlower; d += sblock.fs_frag) {
741			blkno = d / sblock.fs_frag;
742			setblock(&sblock, cg_blksfree(&acg), blkno);
743			if (sblock.fs_contigsumsize > 0)
744				setbit(cg_clustersfree(&acg), blkno);
745			acg.cg_cs.cs_nbfree++;
746			cg_blktot(&acg)[cbtocylno(&sblock, d)]++;
747			cg_blks(&sblock, &acg, cbtocylno(&sblock, d))
748			    [cbtorpos(&sblock, d)]++;
749		}
750		sblock.fs_dsize += dlower;
751	}
752	sblock.fs_dsize += acg.cg_ndblk - dupper;
753	if ((i = dupper % sblock.fs_frag)) {
754		acg.cg_frsum[sblock.fs_frag - i]++;
755		for (d = dupper + sblock.fs_frag - i; dupper < d; dupper++) {
756			setbit(cg_blksfree(&acg), dupper);
757			acg.cg_cs.cs_nffree++;
758		}
759	}
760	for (d = dupper; d + sblock.fs_frag <= dmax - cbase;) {
761		blkno = d / sblock.fs_frag;
762		setblock(&sblock, cg_blksfree(&acg), blkno);
763		if (sblock.fs_contigsumsize > 0)
764			setbit(cg_clustersfree(&acg), blkno);
765		acg.cg_cs.cs_nbfree++;
766		cg_blktot(&acg)[cbtocylno(&sblock, d)]++;
767		cg_blks(&sblock, &acg, cbtocylno(&sblock, d))
768		    [cbtorpos(&sblock, d)]++;
769		d += sblock.fs_frag;
770	}
771	if (d < dmax - cbase) {
772		acg.cg_frsum[dmax - cbase - d]++;
773		for (; d < dmax - cbase; d++) {
774			setbit(cg_blksfree(&acg), d);
775			acg.cg_cs.cs_nffree++;
776		}
777	}
778	if (sblock.fs_contigsumsize > 0) {
779		int32_t *sump = cg_clustersum(&acg);
780		u_char *mapp = cg_clustersfree(&acg);
781		int map = *mapp++;
782		int bit = 1;
783		int run = 0;
784
785		for (i = 0; i < acg.cg_nclusterblks; i++) {
786			if ((map & bit) != 0)
787				run++;
788			else if (run != 0) {
789				if (run > sblock.fs_contigsumsize)
790					run = sblock.fs_contigsumsize;
791				sump[run]++;
792				run = 0;
793			}
794			if ((i & (NBBY - 1)) != NBBY - 1)
795				bit <<= 1;
796			else {
797				map = *mapp++;
798				bit = 1;
799			}
800		}
801		if (run != 0) {
802			if (run > sblock.fs_contigsumsize)
803				run = sblock.fs_contigsumsize;
804			sump[run]++;
805		}
806	}
807	sblock.fs_cstotal.cs_ndir += acg.cg_cs.cs_ndir;
808	sblock.fs_cstotal.cs_nffree += acg.cg_cs.cs_nffree;
809	sblock.fs_cstotal.cs_nbfree += acg.cg_cs.cs_nbfree;
810	sblock.fs_cstotal.cs_nifree += acg.cg_cs.cs_nifree;
811	*cs = acg.cg_cs;
812	wtfs(fsbtodb(&sblock, cgtod(&sblock, cylno)),
813		sblock.fs_bsize, (char *)&acg);
814}
815
816/*
817 * initialize the file system
818 */
819struct dinode node;
820
821#ifdef LOSTDIR
822#define PREDEFDIR 3
823#else
824#define PREDEFDIR 2
825#endif
826
827struct direct root_dir[] = {
828	{ ROOTINO, sizeof(struct direct), DT_DIR, 1, "." },
829	{ ROOTINO, sizeof(struct direct), DT_DIR, 2, ".." },
830#ifdef LOSTDIR
831	{ LOSTFOUNDINO, sizeof(struct direct), DT_DIR, 10, "lost+found" },
832#endif
833};
834struct odirect {
835	u_long	d_ino;
836	u_short	d_reclen;
837	u_short	d_namlen;
838	u_char	d_name[MAXNAMLEN + 1];
839} oroot_dir[] = {
840	{ ROOTINO, sizeof(struct direct), 1, "." },
841	{ ROOTINO, sizeof(struct direct), 2, ".." },
842#ifdef LOSTDIR
843	{ LOSTFOUNDINO, sizeof(struct direct), 10, "lost+found" },
844#endif
845};
846#ifdef LOSTDIR
847struct direct lost_found_dir[] = {
848	{ LOSTFOUNDINO, sizeof(struct direct), DT_DIR, 1, "." },
849	{ ROOTINO, sizeof(struct direct), DT_DIR, 2, ".." },
850	{ 0, DIRBLKSIZ, 0, 0, 0 },
851};
852struct odirect olost_found_dir[] = {
853	{ LOSTFOUNDINO, sizeof(struct direct), 1, "." },
854	{ ROOTINO, sizeof(struct direct), 2, ".." },
855	{ 0, DIRBLKSIZ, 0, 0 },
856};
857#endif
858char buf[MAXBSIZE];
859
860void
861fsinit(time_t utime)
862{
863#ifdef LOSTDIR
864	int i;
865#endif
866
867	/*
868	 * initialize the node
869	 */
870	node.di_atime = utime;
871	node.di_mtime = utime;
872	node.di_ctime = utime;
873#ifdef LOSTDIR
874	/*
875	 * create the lost+found directory
876	 */
877	if (Oflag) {
878		(void)makedir((struct direct *)olost_found_dir, 2);
879		for (i = DIRBLKSIZ; i < sblock.fs_bsize; i += DIRBLKSIZ)
880			memmove(&buf[i], &olost_found_dir[2],
881			    DIRSIZ(0, &olost_found_dir[2]));
882	} else {
883		(void)makedir(lost_found_dir, 2);
884		for (i = DIRBLKSIZ; i < sblock.fs_bsize; i += DIRBLKSIZ)
885			memmove(&buf[i], &lost_found_dir[2],
886			    DIRSIZ(0, &lost_found_dir[2]));
887	}
888	node.di_mode = IFDIR | UMASK;
889	node.di_nlink = 2;
890	node.di_size = sblock.fs_bsize;
891	node.di_db[0] = alloc(node.di_size, node.di_mode);
892	node.di_blocks = btodb(fragroundup(&sblock, node.di_size));
893	wtfs(fsbtodb(&sblock, node.di_db[0]), node.di_size, buf);
894	iput(&node, LOSTFOUNDINO);
895#endif
896	/*
897	 * create the root directory
898	 */
899	node.di_mode = IFDIR | UMASK;
900	node.di_nlink = PREDEFDIR;
901	if (Oflag)
902		node.di_size = makedir((struct direct *)oroot_dir, PREDEFDIR);
903	else
904		node.di_size = makedir(root_dir, PREDEFDIR);
905	node.di_db[0] = alloc(sblock.fs_fsize, node.di_mode);
906	node.di_blocks = btodb(fragroundup(&sblock, node.di_size));
907	wtfs(fsbtodb(&sblock, node.di_db[0]), sblock.fs_fsize, buf);
908	iput(&node, ROOTINO);
909}
910
911/*
912 * construct a set of directory entries in "buf".
913 * return size of directory.
914 */
915int
916makedir(struct direct *protodir, int entries)
917{
918	char *cp;
919	int i, spcleft;
920
921	spcleft = DIRBLKSIZ;
922	for (cp = buf, i = 0; i < entries - 1; i++) {
923		protodir[i].d_reclen = DIRSIZ(0, &protodir[i]);
924		memmove(cp, &protodir[i], protodir[i].d_reclen);
925		cp += protodir[i].d_reclen;
926		spcleft -= protodir[i].d_reclen;
927	}
928	protodir[i].d_reclen = spcleft;
929	memmove(cp, &protodir[i], DIRSIZ(0, &protodir[i]));
930	return (DIRBLKSIZ);
931}
932
933/*
934 * allocate a block or frag
935 */
936daddr_t
937alloc(int size, int mode)
938{
939	int i, frag;
940	daddr_t d, blkno;
941
942	rdfs(fsbtodb(&sblock, cgtod(&sblock, 0)), sblock.fs_cgsize,
943	    (char *)&acg);
944	if (acg.cg_magic != CG_MAGIC) {
945		printf("cg 0: bad magic number\n");
946		return (0);
947	}
948	if (acg.cg_cs.cs_nbfree == 0) {
949		printf("first cylinder group ran out of space\n");
950		return (0);
951	}
952	for (d = 0; d < acg.cg_ndblk; d += sblock.fs_frag)
953		if (isblock(&sblock, cg_blksfree(&acg), d / sblock.fs_frag))
954			goto goth;
955	printf("internal error: can't find block in cyl 0\n");
956	return (0);
957goth:
958	blkno = fragstoblks(&sblock, d);
959	clrblock(&sblock, cg_blksfree(&acg), blkno);
960	if (sblock.fs_contigsumsize > 0)
961		clrbit(cg_clustersfree(&acg), blkno);
962	acg.cg_cs.cs_nbfree--;
963	sblock.fs_cstotal.cs_nbfree--;
964	fscs[0].cs_nbfree--;
965	if (mode & IFDIR) {
966		acg.cg_cs.cs_ndir++;
967		sblock.fs_cstotal.cs_ndir++;
968		fscs[0].cs_ndir++;
969	}
970	cg_blktot(&acg)[cbtocylno(&sblock, d)]--;
971	cg_blks(&sblock, &acg, cbtocylno(&sblock, d))[cbtorpos(&sblock, d)]--;
972	if (size != sblock.fs_bsize) {
973		frag = howmany(size, sblock.fs_fsize);
974		fscs[0].cs_nffree += sblock.fs_frag - frag;
975		sblock.fs_cstotal.cs_nffree += sblock.fs_frag - frag;
976		acg.cg_cs.cs_nffree += sblock.fs_frag - frag;
977		acg.cg_frsum[sblock.fs_frag - frag]++;
978		for (i = frag; i < sblock.fs_frag; i++)
979			setbit(cg_blksfree(&acg), d + i);
980	}
981	wtfs(fsbtodb(&sblock, cgtod(&sblock, 0)), sblock.fs_cgsize,
982	    (char *)&acg);
983	return (d);
984}
985
986/*
987 * Calculate number of inodes per group.
988 */
989long
990calcipg(long cpg, long bpcg, off_t *usedbp)
991{
992	int i;
993	long ipg, new_ipg, ncg, ncyl;
994	off_t usedb;
995
996	/*
997	 * Prepare to scale by fssize / (number of sectors in cylinder groups).
998	 * Note that fssize is still in sectors, not filesystem blocks.
999	 */
1000	ncyl = howmany(fssize, (u_int)secpercyl);
1001	ncg = howmany(ncyl, cpg);
1002	/*
1003	 * Iterate a few times to allow for ipg depending on itself.
1004	 */
1005	ipg = 0;
1006	for (i = 0; i < 10; i++) {
1007		usedb = (sblock.fs_iblkno + ipg / INOPF(&sblock)) *
1008		    NSPF(&sblock) * (off_t)sectorsize;
1009		new_ipg = (cpg * (quad_t)bpcg - usedb) / density *
1010		    fssize / ncg / secpercyl / cpg;
1011		new_ipg = roundup(new_ipg, INOPB(&sblock));
1012		if (new_ipg == ipg)
1013			break;
1014		ipg = new_ipg;
1015	}
1016	*usedbp = usedb;
1017	return (ipg);
1018}
1019
1020/*
1021 * Allocate an inode on the disk
1022 */
1023void
1024iput(struct dinode *ip, ino_t ino)
1025{
1026	struct dinode buf[MAXINOPB];
1027	daddr_t d;
1028	int c;
1029
1030	ip->di_gen = random();
1031	c = ino_to_cg(&sblock, ino);
1032	rdfs(fsbtodb(&sblock, cgtod(&sblock, 0)), sblock.fs_cgsize,
1033	    (char *)&acg);
1034	if (acg.cg_magic != CG_MAGIC) {
1035		printf("cg 0: bad magic number\n");
1036		exit(31);
1037	}
1038	acg.cg_cs.cs_nifree--;
1039	setbit(cg_inosused(&acg), ino);
1040	wtfs(fsbtodb(&sblock, cgtod(&sblock, 0)), sblock.fs_cgsize,
1041	    (char *)&acg);
1042	sblock.fs_cstotal.cs_nifree--;
1043	fscs[0].cs_nifree--;
1044	if (ino >= sblock.fs_ipg * sblock.fs_ncg) {
1045		printf("fsinit: inode value out of range (%d).\n", ino);
1046		exit(32);
1047	}
1048	d = fsbtodb(&sblock, ino_to_fsba(&sblock, ino));
1049	rdfs(d, sblock.fs_bsize, (char *)buf);
1050	buf[ino_to_fsbo(&sblock, ino)] = *ip;
1051	wtfs(d, sblock.fs_bsize, (char *)buf);
1052}
1053
1054/*
1055 * read a block from the file system
1056 */
1057void
1058rdfs(daddr_t bno, int size, char *bf)
1059{
1060	int n;
1061
1062	wtfsflush();
1063	if (lseek(fsi, (off_t)bno * sectorsize, 0) < 0) {
1064		printf("seek error: %ld\n", (long)bno);
1065		err(33, "rdfs");
1066	}
1067	n = read(fsi, bf, size);
1068	if (n != size) {
1069		printf("read error: %ld\n", (long)bno);
1070		err(34, "rdfs");
1071	}
1072}
1073
1074#define WCSIZE (128 * 1024)
1075daddr_t wc_sect;		/* units of sectorsize */
1076int wc_end;			/* bytes */
1077static char wc[WCSIZE];		/* bytes */
1078
1079/*
1080 * Flush dirty write behind buffer.
1081 */
1082void
1083wtfsflush()
1084{
1085	int n;
1086	if (wc_end) {
1087		if (lseek(fso, (off_t)wc_sect * sectorsize, SEEK_SET) < 0) {
1088			printf("seek error: %ld\n", (long)wc_sect);
1089			err(35, "wtfs - writecombine");
1090		}
1091		n = write(fso, wc, wc_end);
1092		if (n != wc_end) {
1093			printf("write error: %ld\n", (long)wc_sect);
1094			err(36, "wtfs - writecombine");
1095		}
1096		wc_end = 0;
1097	}
1098}
1099
1100/*
1101 * write a block to the file system
1102 */
1103void
1104wtfs(daddr_t bno, int size, char *bf)
1105{
1106	int done, n;
1107
1108	if (Nflag)
1109		return;
1110	done = 0;
1111	if (wc_end == 0 && size <= WCSIZE) {
1112		wc_sect = bno;
1113		bcopy(bf, wc, size);
1114		wc_end = size;
1115		if (wc_end < WCSIZE)
1116			return;
1117		done = 1;
1118	}
1119	if ((off_t)wc_sect * sectorsize + wc_end == (off_t)bno * sectorsize &&
1120	    wc_end + size <= WCSIZE) {
1121		bcopy(bf, wc + wc_end, size);
1122		wc_end += size;
1123		if (wc_end < WCSIZE)
1124			return;
1125		done = 1;
1126	}
1127	wtfsflush();
1128	if (done)
1129		return;
1130	if (lseek(fso, (off_t)bno * sectorsize, SEEK_SET) < 0) {
1131		printf("seek error: %ld\n", (long)bno);
1132		err(35, "wtfs");
1133	}
1134	n = write(fso, bf, size);
1135	if (n != size) {
1136		printf("write error: %ld\n", (long)bno);
1137		err(36, "wtfs");
1138	}
1139}
1140
1141/*
1142 * check if a block is available
1143 */
1144int
1145isblock(struct fs *fs, unsigned char *cp, int h)
1146{
1147	unsigned char mask;
1148
1149	switch (fs->fs_frag) {
1150	case 8:
1151		return (cp[h] == 0xff);
1152	case 4:
1153		mask = 0x0f << ((h & 0x1) << 2);
1154		return ((cp[h >> 1] & mask) == mask);
1155	case 2:
1156		mask = 0x03 << ((h & 0x3) << 1);
1157		return ((cp[h >> 2] & mask) == mask);
1158	case 1:
1159		mask = 0x01 << (h & 0x7);
1160		return ((cp[h >> 3] & mask) == mask);
1161	default:
1162		fprintf(stderr, "isblock bad fs_frag %d\n", fs->fs_frag);
1163		return (0);
1164	}
1165}
1166
1167/*
1168 * take a block out of the map
1169 */
1170void
1171clrblock(struct fs *fs, unsigned char *cp, int h)
1172{
1173	switch ((fs)->fs_frag) {
1174	case 8:
1175		cp[h] = 0;
1176		return;
1177	case 4:
1178		cp[h >> 1] &= ~(0x0f << ((h & 0x1) << 2));
1179		return;
1180	case 2:
1181		cp[h >> 2] &= ~(0x03 << ((h & 0x3) << 1));
1182		return;
1183	case 1:
1184		cp[h >> 3] &= ~(0x01 << (h & 0x7));
1185		return;
1186	default:
1187		fprintf(stderr, "clrblock bad fs_frag %d\n", fs->fs_frag);
1188		return;
1189	}
1190}
1191
1192/*
1193 * put a block into the map
1194 */
1195void
1196setblock(struct fs *fs, unsigned char *cp, int h)
1197{
1198	switch (fs->fs_frag) {
1199	case 8:
1200		cp[h] = 0xff;
1201		return;
1202	case 4:
1203		cp[h >> 1] |= (0x0f << ((h & 0x1) << 2));
1204		return;
1205	case 2:
1206		cp[h >> 2] |= (0x03 << ((h & 0x3) << 1));
1207		return;
1208	case 1:
1209		cp[h >> 3] |= (0x01 << (h & 0x7));
1210		return;
1211	default:
1212		fprintf(stderr, "setblock bad fs_frag %d\n", fs->fs_frag);
1213		return;
1214	}
1215}
1216
1217/*
1218 * Determine the number of characters in a
1219 * single line.
1220 */
1221
1222static int
1223charsperline()
1224{
1225	int columns;
1226	char *cp;
1227	struct winsize ws;
1228
1229	columns = 0;
1230	if (ioctl(0, TIOCGWINSZ, &ws) != -1)
1231		columns = ws.ws_col;
1232	if (columns == 0 && (cp = getenv("COLUMNS")))
1233		columns = atoi(cp);
1234	if (columns == 0)
1235		columns = 80;	/* last resort */
1236	return (columns);
1237}
1238