xref: /original-bsd/sbin/newfs/newfs.c (revision 1eabc47f)
1 /*
2  * Copyright (c) 1983, 1989 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 static char sccsid[] = "@(#)newfs.c	6.20 (Berkeley) 10/18/89";
20 #endif /* not lint */
21 
22 #ifndef lint
23 char copyright[] =
24 "@(#) Copyright (c) 1983, 1989 Regents of the University of California.\n\
25  All rights reserved.\n";
26 #endif /* not lint */
27 
28 /*
29  * newfs: friendly front end to mkfs
30  */
31 #include <sys/param.h>
32 #include <sys/stat.h>
33 #include <ufs/fs.h>
34 #include <ufs/dir.h>
35 #include <sys/ioctl.h>
36 #include <sys/disklabel.h>
37 #include <sys/file.h>
38 #include <sys/mount.h>
39 
40 #include <stdio.h>
41 #include <ctype.h>
42 #include <paths.h>
43 
44 #define COMPAT			/* allow non-labeled disks */
45 
46 /*
47  * The following two constants set the default block and fragment sizes.
48  * Both constants must be a power of 2 and meet the following constraints:
49  *	MINBSIZE <= DESBLKSIZE <= MAXBSIZE
50  *	sectorsize <= DESFRAGSIZE <= DESBLKSIZE
51  *	DESBLKSIZE / DESFRAGSIZE <= 8
52  */
53 #define	DFL_FRAGSIZE	1024
54 #define	DFL_BLKSIZE	8192
55 
56 /*
57  * Cylinder groups may have up to many cylinders. The actual
58  * number used depends upon how much information can be stored
59  * on a single cylinder. The default is to use 16 cylinders
60  * per group.
61  */
62 #define	DESCPG		16	/* desired fs_cpg */
63 
64 /*
65  * MINFREE gives the minimum acceptable percentage of file system
66  * blocks which may be free. If the freelist drops below this level
67  * only the superuser may continue to allocate blocks. This may
68  * be set to 0 if no reserve of free blocks is deemed necessary,
69  * however throughput drops by fifty percent if the file system
70  * is run at between 90% and 100% full; thus the default value of
71  * fs_minfree is 10%. With 10% free space, fragmentation is not a
72  * problem, so we choose to optimize for time.
73  */
74 #define MINFREE		10
75 #define DEFAULTOPT	FS_OPTTIME
76 
77 /*
78  * ROTDELAY gives the minimum number of milliseconds to initiate
79  * another disk transfer on the same cylinder. It is used in
80  * determining the rotationally optimal layout for disk blocks
81  * within a file; the default of fs_rotdelay is 4ms.
82  */
83 #define ROTDELAY	4
84 
85 /*
86  * MAXCONTIG sets the default for the maximum number of blocks
87  * that may be allocated sequentially. Since UNIX drivers are
88  * not capable of scheduling multi-block transfers, this defaults
89  * to 1 (ie no contiguous blocks are allocated).
90  */
91 #define MAXCONTIG	1
92 
93 /*
94  * MAXBLKPG determines the maximum number of data blocks which are
95  * placed in a single cylinder group. The default is one indirect
96  * block worth of data blocks.
97  */
98 #define MAXBLKPG(bsize)	((bsize) / sizeof(daddr_t))
99 
100 /*
101  * Each file system has a number of inodes statically allocated.
102  * We allocate one inode slot per NFPI fragments, expecting this
103  * to be far more than we will ever need.
104  */
105 #define	NFPI		4
106 
107 /*
108  * For each cylinder we keep track of the availability of blocks at different
109  * rotational positions, so that we can lay out the data to be picked
110  * up with minimum rotational latency.  NRPOS is the default number of
111  * rotational positions that we distinguish.  With NRPOS of 8 the resolution
112  * of our summary information is 2ms for a typical 3600 rpm drive.
113  */
114 #define	NRPOS		8	/* number distinct rotational positions */
115 
116 
117 int	mfs;			/* run as the memory based filesystem */
118 int	Nflag;			/* run without writing file system */
119 int	fssize;			/* file system size */
120 int	ntracks;		/* # tracks/cylinder */
121 int	nsectors;		/* # sectors/track */
122 int	nphyssectors;		/* # sectors/track including spares */
123 int	secpercyl;		/* sectors per cylinder */
124 int	trackspares = -1;	/* spare sectors per track */
125 int	cylspares = -1;		/* spare sectors per cylinder */
126 int	sectorsize;		/* bytes/sector */
127 #ifdef tahoe
128 int	realsectorsize;		/* bytes/sector in hardware */
129 #endif
130 int	rpm;			/* revolutions/minute of drive */
131 int	interleave;		/* hardware sector interleave */
132 int	trackskew = -1;		/* sector 0 skew, per track */
133 int	headswitch;		/* head switch time, usec */
134 int	trackseek;		/* track-to-track seek, usec */
135 int	fsize = 0;		/* fragment size */
136 int	bsize = 0;		/* block size */
137 int	cpg = DESCPG;		/* cylinders/cylinder group */
138 int	cpgflg;			/* cylinders/cylinder group flag was given */
139 int	minfree = MINFREE;	/* free space threshold */
140 int	opt = DEFAULTOPT;	/* optimization preference (space or time) */
141 int	density;		/* number of bytes per inode */
142 int	maxcontig = MAXCONTIG;	/* max contiguous blocks to allocate */
143 int	rotdelay = ROTDELAY;	/* rotational delay between blocks */
144 int	maxbpg;			/* maximum blocks per file in a cyl group */
145 int	nrpos = NRPOS;		/* # of distinguished rotational positions */
146 int	bbsize = BBSIZE;	/* boot block size */
147 int	sbsize = SBSIZE;	/* superblock size */
148 int	mntflags;		/* flags to be passed to mount */
149 u_long	memleft;		/* virtual memory available */
150 caddr_t	membase;		/* start address of memory based filesystem */
151 #ifdef COMPAT
152 int	unlabelled;
153 #endif
154 
155 char	device[MAXPATHLEN];
156 char	*progname;
157 
158 extern	int errno;
159 char	*index();
160 char	*rindex();
161 
162 main(argc, argv)
163 	int argc;
164 	char *argv[];
165 {
166 	char *cp, *special, *rindex();
167 	register struct partition *pp;
168 	register struct disklabel *lp;
169 	struct disklabel *getdisklabel();
170 	struct partition oldpartition;
171 	struct mfs_args args;
172 	struct stat st;
173 	int fsi, fso;
174 	register int i;
175 	int status;
176 	char buf[BUFSIZ];
177 
178 	if ((progname = rindex(*argv, '/') + 1) == (char *)1)
179 		progname = *argv;
180 	if (!strcmp(progname, "mfs")) {
181 		Nflag++;
182 		mfs++;
183 	}
184 	argc--, argv++;
185 	while (argc > 0 && argv[0][0] == '-') {
186 		for (cp = &argv[0][1]; *cp; cp++)
187 			switch (*cp) {
188 
189 			case 'F':
190 				if (!mfs)
191 					fatal("-F: unknown flag");
192 				if (argc < 1)
193 					fatal("-F: mount flags");
194 				argc--, argv++;
195 				mntflags = atoi(*argv);
196 				if (mntflags == 0)
197 					fatal("%s: bad mount flags", *argv);
198 				goto next;
199 
200 			case 'N':
201 				Nflag++;
202 				break;
203 
204 			case 'S':
205 				if (argc < 1)
206 					fatal("-S: missing sector size");
207 				argc--, argv++;
208 				sectorsize = atoi(*argv);
209 				if (sectorsize <= 0)
210 					fatal("%s: bad sector size", *argv);
211 				goto next;
212 
213 			case 'a':
214 				if (argc < 1)
215 					fatal("-a: missing max contiguous blocks\n");
216 				argc--, argv++;
217 				maxcontig = atoi(*argv);
218 				if (maxcontig <= 0)
219 					fatal("%s: bad max contiguous blocks\n",
220 						*argv);
221 				goto next;
222 
223 			case 'b':
224 				if (argc < 1)
225 					fatal("-b: missing block size");
226 				argc--, argv++;
227 				bsize = atoi(*argv);
228 				if (bsize < MINBSIZE)
229 					fatal("%s: bad block size", *argv);
230 				goto next;
231 
232 			case 'c':
233 				if (argc < 1)
234 					fatal("-c: missing cylinders/group");
235 				argc--, argv++;
236 				cpg = atoi(*argv);
237 				if (cpg <= 0)
238 					fatal("%s: bad cylinders/group", *argv);
239 				cpgflg++;
240 				goto next;
241 
242 			case 'd':
243 				if (argc < 1)
244 					fatal("-d: missing rotational delay\n");
245 				argc--, argv++;
246 				rotdelay = atoi(*argv);
247 				if (rotdelay < 0)
248 					fatal("%s: bad rotational delay\n",
249 						*argv);
250 				goto next;
251 
252 			case 'e':
253 				if (argc < 1)
254 					fatal("-e: missing blocks pre file in a cyl group\n");
255 				argc--, argv++;
256 				maxbpg = atoi(*argv);
257 				if (maxbpg <= 0)
258 					fatal("%s: bad blocks per file in a cyl group\n",
259 						*argv);
260 				goto next;
261 
262 			case 'f':
263 				if (argc < 1)
264 					fatal("-f: missing frag size");
265 				argc--, argv++;
266 				fsize = atoi(*argv);
267 				if (fsize <= 0)
268 					fatal("%s: bad frag size", *argv);
269 				goto next;
270 
271 			case 'i':
272 				if (argc < 1)
273 					fatal("-i: missing bytes per inode\n");
274 				argc--, argv++;
275 				density = atoi(*argv);
276 				if (density <= 0)
277 					fatal("%s: bad bytes per inode\n",
278 						*argv);
279 				goto next;
280 
281 			case 'k':
282 				if (argc < 1)
283 					fatal("-k: track skew");
284 				argc--, argv++;
285 				trackskew = atoi(*argv);
286 				if (trackskew < 0)
287 					fatal("%s: bad track skew", *argv);
288 				goto next;
289 
290 			case 'l':
291 				if (argc < 1)
292 					fatal("-l: interleave");
293 				argc--, argv++;
294 				interleave = atoi(*argv);
295 				if (interleave <= 0)
296 					fatal("%s: bad interleave", *argv);
297 				goto next;
298 
299 			case 'm':
300 				if (argc < 1)
301 					fatal("-m: missing free space %%\n");
302 				argc--, argv++;
303 				minfree = atoi(*argv);
304 				if (minfree < 0 || minfree > 99)
305 					fatal("%s: bad free space %%\n",
306 						*argv);
307 				goto next;
308 
309 			case 'n':
310 				if (argc < 1)
311 					fatal("-n: missing rotational layout count\n");
312 				argc--, argv++;
313 				nrpos = atoi(*argv);
314 				if (nrpos <= 0)
315 					fatal("%s: bad rotational layout count\n",
316 						*argv);
317 				goto next;
318 
319 			case 'o':
320 				if (argc < 1)
321 					fatal("-o: missing optimization preference");
322 				argc--, argv++;
323 				if (strcmp(*argv, "space") == 0)
324 					opt = FS_OPTSPACE;
325 				else if (strcmp(*argv, "time") == 0)
326 					opt = FS_OPTTIME;
327 				else
328 					fatal("%s: bad optimization preference %s",
329 					    *argv,
330 					    "(options are `space' or `time')");
331 				goto next;
332 
333 			case 'p':
334 				if (argc < 1)
335 					fatal("-p: spare sectors per track");
336 				argc--, argv++;
337 				trackspares = atoi(*argv);
338 				if (trackspares < 0)
339 					fatal("%s: bad spare sectors per track", *argv);
340 				goto next;
341 
342 			case 'r':
343 				if (argc < 1)
344 					fatal("-r: missing revs/minute\n");
345 				argc--, argv++;
346 				rpm = atoi(*argv);
347 				if (rpm <= 0)
348 					fatal("%s: bad revs/minute\n", *argv);
349 				goto next;
350 
351 			case 's':
352 				if (argc < 1)
353 					fatal("-s: missing file system size");
354 				argc--, argv++;
355 				fssize = atoi(*argv);
356 				if (fssize <= 0)
357 					fatal("%s: bad file system size",
358 						*argv);
359 				goto next;
360 
361 			case 't':
362 				if (argc < 1)
363 					fatal("-t: missing track total");
364 				argc--, argv++;
365 				ntracks = atoi(*argv);
366 				if (ntracks <= 0)
367 					fatal("%s: bad total tracks", *argv);
368 				goto next;
369 
370 			case 'u':
371 				if (argc < 1)
372 					fatal("-u: missing sectors/track");
373 				argc--, argv++;
374 				nsectors = atoi(*argv);
375 				if (nsectors <= 0)
376 					fatal("%s: bad sectors/track", *argv);
377 				goto next;
378 
379 			case 'x':
380 				if (argc < 1)
381 					fatal("-x: spare sectors per cylinder");
382 				argc--, argv++;
383 				cylspares = atoi(*argv);
384 				if (cylspares < 0)
385 					fatal("%s: bad spare sectors per cylinder", *argv);
386 				goto next;
387 
388 			default:
389 				fatal("-%c: unknown flag", *cp);
390 			}
391 next:
392 		argc--, argv++;
393 	}
394 	if (argc < 1) {
395 		if (mfs)
396 			fprintf(stderr,
397 			    "usage: mfs [ fsoptions ] special-device %s\n",
398 			    "mount-point");
399 		else
400 #ifdef COMPAT
401 			fprintf(stderr, "usage: %s\n",
402 			    "newfs [ fsoptions ] special-device [device-type]");
403 #else
404 			fprintf(stderr,
405 			    "usage: newfs [ fsoptions ] special-device\n");
406 #endif
407 		fprintf(stderr, "where fsoptions are:\n");
408 		fprintf(stderr, "\t-N do not create file system, %s\n",
409 			"just print out parameters");
410 		fprintf(stderr, "\t-b block size\n");
411 		fprintf(stderr, "\t-f frag size\n");
412 		fprintf(stderr, "\t-m minimum free space %%\n");
413 		fprintf(stderr, "\t-o optimization preference %s\n",
414 			"(`space' or `time')");
415 		fprintf(stderr, "\t-a maximum contiguous blocks\n");
416 		fprintf(stderr, "\t-d rotational delay between %s\n",
417 			"contiguous blocks");
418 		fprintf(stderr, "\t-e maximum blocks per file in a %s\n",
419 			"cylinder group");
420 		fprintf(stderr, "\t-i number of bytes per inode\n");
421 		fprintf(stderr, "\t-c cylinders/group\n");
422 		fprintf(stderr, "\t-n number of distinguished %s\n",
423 			"rotational positions");
424 		fprintf(stderr, "\t-s file system size (sectors)\n");
425 		fprintf(stderr, "\t-r revolutions/minute\n");
426 		fprintf(stderr, "\t-S sector size\n");
427 		fprintf(stderr, "\t-u sectors/track\n");
428 		fprintf(stderr, "\t-t tracks/cylinder\n");
429 		fprintf(stderr, "\t-p spare sectors per track\n");
430 		fprintf(stderr, "\t-x spare sectors per cylinder\n");
431 		fprintf(stderr, "\t-l hardware sector interleave\n");
432 		fprintf(stderr, "\t-k sector 0 skew, per track\n");
433 		exit(1);
434 	}
435 	special = argv[0];
436 	cp = rindex(special, '/');
437 	if (cp != 0)
438 		special = cp + 1;
439 	if (*special == 'r' && special[1] != 'a' && special[1] != 'b')
440 		special++;
441 	(void)sprintf(device, "%s/r%s", _PATH_DEV, special);
442 	special = device;
443 	if (!Nflag) {
444 		fso = open(special, O_WRONLY);
445 		if (fso < 0) {
446 			perror(special);
447 			exit(2);
448 		}
449 	} else
450 		fso = -1;
451 	fsi = open(special, O_RDONLY);
452 	if (fsi < 0) {
453 		perror(special);
454 		exit(3);
455 	}
456 	if (fstat(fsi, &st) < 0) {
457 		fprintf(stderr, "%s: ", progname); perror(special);
458 		exit(4);
459 	}
460 	if ((st.st_mode & S_IFMT) != S_IFCHR)
461 		fatal("%s: not a character device", special);
462 	cp = index(argv[0], '\0') - 1;
463 	if (cp == 0 || (*cp < 'a' || *cp > 'h') && !isdigit(*cp))
464 		fatal("%s: can't figure out file system partition", argv[0]);
465 #ifdef COMPAT
466 	lp = getdisklabel(special, fsi, argv[1]);
467 #else
468 	lp = getdisklabel(special, fsi);
469 #endif
470 	if (isdigit(*cp))
471 		pp = &lp->d_partitions[0];
472 	else
473 		pp = &lp->d_partitions[*cp - 'a'];
474 	if (pp->p_size == 0)
475 		fatal("%s: `%c' partition is unavailable", argv[0], *cp);
476 	if (fssize == 0)
477 		fssize = pp->p_size;
478 	if (fssize > pp->p_size && !mfs)
479 	       fatal("%s: maximum file system size on the `%c' partition is %d",
480 			argv[0], *cp, pp->p_size);
481 	if (rpm == 0) {
482 		rpm = lp->d_rpm;
483 		if (rpm <= 0)
484 			rpm = 3600;
485 	}
486 	if (ntracks == 0) {
487 		ntracks = lp->d_ntracks;
488 		if (ntracks <= 0)
489 			fatal("%s: no default #tracks", argv[0]);
490 	}
491 	if (nsectors == 0) {
492 		nsectors = lp->d_nsectors;
493 		if (nsectors <= 0)
494 			fatal("%s: no default #sectors/track", argv[0]);
495 	}
496 	if (sectorsize == 0) {
497 		sectorsize = lp->d_secsize;
498 		if (sectorsize <= 0)
499 			fatal("%s: no default sector size", argv[0]);
500 	}
501 	if (trackskew == -1) {
502 		trackskew = lp->d_trackskew;
503 		if (trackskew < 0)
504 			trackskew = 0;
505 	}
506 	if (interleave == 0) {
507 		interleave = lp->d_interleave;
508 		if (interleave <= 0)
509 			interleave = 1;
510 	}
511 	if (fsize == 0) {
512 		fsize = pp->p_fsize;
513 		if (fsize <= 0)
514 			fsize = MAX(DFL_FRAGSIZE, lp->d_secsize);
515 	}
516 	if (bsize == 0) {
517 		bsize = pp->p_frag * pp->p_fsize;
518 		if (bsize <= 0)
519 			bsize = MIN(DFL_BLKSIZE, 8 * fsize);
520 	}
521 	if (density == 0)
522 		density = NFPI * fsize;
523 	if (minfree < 10 && opt != FS_OPTSPACE) {
524 		fprintf(stderr, "Warning: changing optimization to space ");
525 		fprintf(stderr, "because minfree is less than 10%%\n");
526 		opt = FS_OPTSPACE;
527 	}
528 	if (trackspares == -1) {
529 		trackspares = lp->d_sparespertrack;
530 		if (trackspares < 0)
531 			trackspares = 0;
532 	}
533 	nphyssectors = nsectors + trackspares;
534 	if (cylspares == -1) {
535 		cylspares = lp->d_sparespercyl;
536 		if (cylspares < 0)
537 			cylspares = 0;
538 	}
539 	secpercyl = nsectors * ntracks - cylspares;
540 	if (secpercyl != lp->d_secpercyl)
541 		fprintf(stderr, "%s (%d) %s (%d)\n",
542 			"Warning: calculated sectors per cylinder", secpercyl,
543 			"disagrees with disk label", lp->d_secpercyl);
544 	if (maxbpg == 0)
545 		maxbpg = MAXBLKPG(bsize);
546 	headswitch = lp->d_headswitch;
547 	trackseek = lp->d_trkseek;
548 	bbsize = lp->d_bbsize;
549 	sbsize = lp->d_sbsize;
550 	oldpartition = *pp;
551 #ifdef tahoe
552 	realsectorsize = sectorsize;
553 	if (sectorsize != DEV_BSIZE) {		/* XXX */
554 		int secperblk = DEV_BSIZE / sectorsize;
555 
556 		sectorsize = DEV_BSIZE;
557 		nsectors /= secperblk;
558 		nphyssectors /= secperblk;
559 		secpercyl /= secperblk;
560 		fssize /= secperblk;
561 		pp->p_size /= secperblk;
562 	}
563 #endif
564 	mkfs(pp, special, fsi, fso);
565 #ifdef tahoe
566 	if (realsectorsize != DEV_BSIZE)
567 		pp->p_size *= DEV_BSIZE / realsectorsize;
568 #endif
569 	if (!Nflag && bcmp(pp, &oldpartition, sizeof(oldpartition)))
570 		rewritelabel(special, fso, lp);
571 	if (!Nflag)
572 		close(fso);
573 	close(fsi);
574 	if (mfs) {
575 		sprintf(buf, "mfs:%d", getpid());
576 		args.name = buf;
577 		args.base = membase;
578 		args.size = fssize * sectorsize;
579 		if (mount(MOUNT_MFS, argv[1], mntflags, &args) < 0) {
580 			perror("mfs: mount");
581 			exit(5);
582 		}
583 	}
584 	exit(0);
585 }
586 
587 #ifdef COMPAT
588 struct disklabel *
589 getdisklabel(s, fd, type)
590 	char *s, *type;
591 	int fd;
592 {
593 	static struct disklabel lab;
594 	struct disklabel *getdiskbyname();
595 
596 	if (ioctl(fd, DIOCGDINFO, (char *)&lab) < 0) {
597 		if (type == NULL) {
598 			perror("ioctl (GDINFO)");
599 			fatal(
600 		   "%s: can't read disk label; disk type must be specified", s);
601 		}
602 		unlabelled++;
603 		return (getdiskbyname(type));
604 	}
605 	return (&lab);
606 }
607 #else
608 struct disklabel *
609 getdisklabel(s, fd)
610 	char *s;
611 	int fd;
612 {
613 	static struct disklabel lab;
614 
615 	if (ioctl(fd, DIOCGDINFO, (char *)&lab) < 0) {
616 		perror("ioctl (GDINFO)");
617 		fatal("%s: can't read disk label", s);
618 	}
619 	return (&lab);
620 }
621 #endif
622 
623 rewritelabel(s, fd, lp)
624 	char *s;
625 	int fd;
626 	register struct disklabel *lp;
627 {
628 
629 #ifdef COMPAT
630 	if (unlabelled)
631 		return;
632 #endif
633 	lp->d_checksum = 0;
634 	lp->d_checksum = dkcksum(lp);
635 	if (ioctl(fd, DIOCWDINFO, (char *)lp) < 0) {
636 		perror("ioctl (WDINFO)");
637 		fatal("%s: can't rewrite disk label", s);
638 	}
639 #if vax
640 	if (lp->d_type == DTYPE_SMD && lp->d_flags & D_BADSECT) {
641 		register i;
642 		int cfd;
643 		daddr_t alt;
644 		char specname[64];
645 		char blk[1024];
646 		char *cp;
647 
648 		/*
649 		 * Make name for 'c' partition.
650 		 */
651 		strcpy(specname, s);
652 		cp = specname + strlen(specname) - 1;
653 		if (!isdigit(*cp))
654 			*cp = 'c';
655 		cfd = open(specname, O_WRONLY);
656 		if (cfd < 0) {
657 			perror(specname);
658 			exit(6);
659 		}
660 		bzero(blk, sizeof(blk));
661 		*(struct disklabel *)(blk + LABELOFFSET) = *lp;
662 		alt = lp->d_ncylinders * lp->d_secpercyl - lp->d_nsectors;
663 		for (i = 1; i < 11 && i < lp->d_nsectors; i += 2) {
664 			if (lseek(cfd, (off_t)(alt + i) * lp->d_secsize, L_SET) == -1) {
665 				perror("lseek to badsector area");
666 				exit(7);
667 			}
668 			if (write(cfd, blk, lp->d_secsize) < lp->d_secsize) {
669 				int oerrno = errno;
670 				fprintf(stderr, "alternate label %d ", i/2);
671 				errno = oerrno;
672 				perror("write");
673 			}
674 		}
675 		close(cfd);
676 	}
677 #endif
678 }
679 
680 /*VARARGS*/
681 fatal(fmt, arg1, arg2)
682 	char *fmt;
683 {
684 
685 	fprintf(stderr, "%s: ", progname);
686 	fprintf(stderr, fmt, arg1, arg2);
687 	putc('\n', stderr);
688 	exit(8);
689 }
690