xref: /openbsd-src/sbin/newfs_ext2fs/newfs_ext2fs.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /* $OpenBSD: newfs_ext2fs.c,v 1.6 2010/05/18 04:41:14 dlg Exp $ */
2 /*	$NetBSD: newfs_ext2fs.c,v 1.8 2009/03/02 10:38:13 tsutsui Exp $	*/
3 
4 /*
5  * Copyright (c) 1983, 1989, 1993, 1994
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/cdefs.h>
34 
35 /*
36  * newfs: friendly front end to mke2fs
37  */
38 #include <sys/param.h>
39 #include <sys/ioctl.h>
40 #include <sys/dkio.h>
41 #include <sys/disklabel.h>
42 #include <sys/disk.h>
43 #include <sys/file.h>
44 #include <sys/mount.h>
45 
46 #include <ufs/ext2fs/ext2fs.h>
47 #include <ufs/ext2fs/ext2fs_dinode.h>
48 
49 #include <ctype.h>
50 #include <err.h>
51 #include <errno.h>
52 #include <inttypes.h>
53 #include <limits.h>
54 #include <paths.h>
55 #include <stdint.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <unistd.h>
60 #include <util.h>
61 
62 #include "extern.h"
63 
64 static int64_t strsuftoi64(const char *, const char *, int64_t, int64_t, int *);
65 static void usage(void) __dead;
66 
67 /*
68  * For file systems smaller than SMALL_FSSIZE we use the S_DFL_* defaults,
69  * otherwise if less than MEDIUM_FSSIZE use M_DFL_*, otherwise use
70  * L_DFL_*.
71  */
72 #define SMALL_FSSIZE	((4 * 1024 * 1024) / sectorsize)	/* 4MB */
73 #define S_DFL_BSIZE	1024
74 #define MEDIUM_FSSIZE	((512 * 1024 * 1024) / sectorsize)	/* 512MB */
75 #define M_DFL_BSIZE	1024
76 #define L_DFL_BSIZE	4096
77 
78 /*
79  * Each file system has a number of inodes statically allocated.
80  * We allocate one inode slot per 2, 4, or 8 blocks, expecting this
81  * to be far more than we will ever need.
82  */
83 #define S_DFL_NINODE(blocks)	((blocks) / 8)
84 #define M_DFL_NINODE(blocks)	((blocks) / 4)
85 #define L_DFL_NINODE(blocks)	((blocks) / 2)
86 
87 /*
88  * Default sector size.
89  */
90 #define	DFL_SECSIZE	512
91 
92 int	Nflag;			/* run without writing file system */
93 int	Oflag = 0;		/* format as conservative REV0 by default */
94 int	verbosity;		/* amount of printf() output */
95 #define DEFAULT_VERBOSITY 4	/* 4 is traditional behavior of newfs(8) */
96 int64_t fssize;			/* file system size */
97 uint	sectorsize;		/* bytes/sector */
98 uint16_t inodesize = EXT2_REV0_DINODE_SIZE;	/* inode size */
99 uint	fsize = 0;		/* fragment size */
100 uint	bsize = 0;		/* block size */
101 uint	minfree = MINFREE;	/* free space threshold */
102 uint	density;		/* number of bytes per inode */
103 uint	num_inodes;		/* number of inodes (overrides density) */
104 char	*volname = NULL;	/* volume name */
105 
106 static char *disktype = NULL;
107 static char device[MAXPATHLEN];
108 
109 struct disklabel *getdisklabel(const char *, int);
110 struct partition *getpartition(int, const char *, char *[], struct disklabel **);
111 
112 int
113 main(int argc, char *argv[])
114 {
115 	struct statfs *mp;
116 	struct stat sb;
117 	int ch, fsi, fso, len, n, Fflag, Iflag, Zflag;
118 	char *cp, *s1, *s2, *special;
119 	const char *opstring;
120 	int byte_sized;
121 	uint blocks;			/* number of blocks */
122 	struct partition *pp = NULL;
123 	struct disklabel *lp;
124 
125 	cp = NULL;
126 	fsi = fso = -1;
127 	Fflag = Iflag = Zflag = 0;
128 	verbosity = -1;
129 	opstring = "D:FINO:S:V:Zb:f:i:l:m:n:qs:t:v:";
130 	byte_sized = 0;
131 	while ((ch = getopt(argc, argv, opstring)) != -1)
132 		switch (ch) {
133 		case 'D':
134 			inodesize = (uint16_t)strtol(optarg, &s1, 0);
135 			if (*s1 || (inodesize != 128 && inodesize != 256))
136 				errx(1, "Bad inode size %d "
137 				    "(only 128 and 256 supported)", inodesize);
138 			break;
139 		case 'F':
140 			Fflag = 1;
141 			break;
142 		case 'I':
143 			Iflag = 1;
144 			break;
145 		case 'N':
146 			Nflag = 1;
147 			if (verbosity == -1)
148 				verbosity = DEFAULT_VERBOSITY;
149 			break;
150 		case 'O':
151 			Oflag = strsuftoi64("format", optarg, 0, 1, NULL);
152 			break;
153 		case 'S':
154 			/*
155 			 * XXX:
156 			 * non-512 byte sectors almost certainly don't work.
157 			 */
158 			sectorsize = strsuftoi64("sector size",
159 			    optarg, 512, 65536, NULL);
160 			if (!powerof2(sectorsize))
161 				errx(EXIT_FAILURE,
162 				    "sector size `%s' is not a power of 2.",
163 				    optarg);
164 			break;
165 		case 'V':
166 			verbosity = strsuftoi64("verbose", optarg, 0, 4, NULL);
167 			break;
168 		case 'Z':
169 			Zflag = 1;
170 			break;
171 		case 'b':
172 			bsize = strsuftoi64("block size",
173 			    optarg, MINBSIZE, EXT2_MAXBSIZE, NULL);
174 			break;
175 		case 'f':
176 			fsize = strsuftoi64("fragment size",
177 			    optarg, MINBSIZE, EXT2_MAXBSIZE, NULL);
178 			break;
179 		case 'i':
180 			density = strsuftoi64("bytes per inode",
181 			    optarg, 1, INT_MAX, NULL);
182 			break;
183 		case 'm':
184 			minfree = strsuftoi64("free space %",
185 			    optarg, 0, 99, NULL);
186 			break;
187 		case 'n':
188 			num_inodes = strsuftoi64("number of inodes",
189 			    optarg, 1, INT_MAX, NULL);
190 			break;
191 		case 'q':
192 			verbosity = 1;
193 			break;
194 		case 's':
195 			fssize = strsuftoi64("file system size",
196 			    optarg, INT64_MIN, INT64_MAX, &byte_sized);
197 			break;
198 		case 't':
199 			/* compat with newfs -t */
200 			break;
201 		case 'v':
202 			volname = optarg;
203 			if (volname[0] == '\0')
204 				errx(EXIT_FAILURE,
205 				    "Volume name cannot be zero length");
206 			break;
207 		case '?':
208 		default:
209 			usage();
210 		}
211 	argc -= optind;
212 	argv += optind;
213 
214 	if (verbosity == -1)
215 		/* Default to showing cg info */
216 		verbosity = DEFAULT_VERBOSITY;
217 
218 	if (argc != 1)
219 		usage();
220 
221 	memset(&sb, 0, sizeof(sb));
222 	special = argv[0];
223 	if (Fflag) {
224 		int fl;
225 		/*
226 		 * It's a file system image
227 		 * no label, use fixed default for sectorsize.
228 		 */
229 		if (sectorsize == 0)
230 			sectorsize = DFL_SECSIZE;
231 
232 		/* creating image in a regular file */
233 		if (Nflag)
234 			fl = O_RDONLY;
235 		else {
236 			if (fssize > 0)
237 				fl = O_RDWR | O_CREAT;
238 			else
239 				fl = O_RDWR;
240 		}
241 		fsi = open(special, fl, 0777);
242 		if (fsi == -1)
243 			err(EXIT_FAILURE, "can't open file %s", special);
244 		if (fstat(fsi, &sb) == -1)
245 			err(EXIT_FAILURE, "can't fstat opened %s", special);
246 		if (!Nflag)
247 			fso = fsi;
248 	} else {	/* !Fflag */
249 		cp = strrchr(special, '/');
250 		if (cp == NULL) {
251 			struct stat st;
252 			/*
253 			 * No path prefix; try /dev/r%s then /dev/%s.
254 			 */
255 			(void)snprintf(device, sizeof(device), "%sr%s",
256 			    _PATH_DEV, special);
257 			if (stat(device, &st) == -1)
258 				(void)snprintf(device, sizeof(device), "%s%s",
259 				    _PATH_DEV, special);
260 			special = device;
261 		}
262 
263 		fsi = open(special, O_RDONLY);
264 		if (fsi < 0 || fstat(fsi, &sb) == -1)
265 			err(EXIT_FAILURE, "%s: open for read", special);
266 
267 		if (!Nflag) {
268 			fso = open(special, O_WRONLY, 0);
269 			if (fso < 0)
270 				err(EXIT_FAILURE,
271 				    "%s: open for write", special);
272 
273 			/* Bail if target special is mounted */
274 			n = getmntinfo(&mp, MNT_NOWAIT);
275 			if (n == 0)
276 				err(EXIT_FAILURE, "%s: getmntinfo", special);
277 
278 			len = sizeof(_PATH_DEV) - 1;
279 			s1 = special;
280 			if (strncmp(_PATH_DEV, s1, len) == 0)
281 				s1 += len;
282 
283 			while (--n >= 0) {
284 				s2 = mp->f_mntfromname;
285 				if (strncmp(_PATH_DEV, s2, len) == 0) {
286 					s2 += len - 1;
287 					*s2 = 'r';
288 				}
289 				if (strcmp(s1, s2) == 0 ||
290 				    strcmp(s1, &s2[1]) == 0)
291 					errx(EXIT_FAILURE,
292 					    "%s is mounted on %s",
293 					    special, mp->f_mntonname);
294 				++mp;
295 			}
296 		}
297 
298 		pp = getpartition(fsi, special, argv, &lp);
299 		if (!Iflag) {
300 			static const char m[] =
301 			    "%s partition type is not `%s' (or use -I)";
302 			if (pp->p_fstype != FS_EXT2FS)
303 				errx(EXIT_FAILURE, m, special, "Linux Ext2");
304 		}
305 		if (sectorsize == 0) {
306 			sectorsize = lp->d_secsize;
307 			if (sectorsize <= 0)
308 				errx(EXIT_FAILURE, "no default sector size");
309 		}
310 	}
311 
312 	if (byte_sized)
313 		fssize /= sectorsize;
314 	if (fssize <= 0) {
315 		if (sb.st_size != 0)
316 			fssize += sb.st_size / sectorsize;
317 		else if (pp)
318 			fssize += DL_GETPSIZE(pp);
319 		if (fssize <= 0)
320 			errx(EXIT_FAILURE,
321 			    "Unable to determine file system size");
322 	}
323 
324 	/* XXXLUKEM: only ftruncate() regular files ? (dsl: or at all?) */
325 	if (Fflag && fso != -1
326 	    && ftruncate(fso, (off_t)fssize * sectorsize) == -1)
327 		err(1, "can't ftruncate %s to %" PRId64, special, fssize);
328 
329 	if (Zflag && fso != -1) {	/* pre-zero (and de-sparce) the file */
330 		char *buf;
331 		int bufsize, i;
332 		off_t bufrem;
333 		struct statfs sfs;
334 
335 		if (fstatfs(fso, &sfs) == -1) {
336 			warn("can't fstatvfs `%s'", special);
337 			bufsize = 8192;
338 		} else
339 			bufsize = sfs.f_iosize;
340 
341 		if ((buf = calloc(1, bufsize)) == NULL)
342 			err(1, "can't malloc buffer of %d",
343 			bufsize);
344 		bufrem = fssize * sectorsize;
345 		if (verbosity > 0)
346 			printf("Creating file system image in `%s', "
347 			    "size %" PRId64 " bytes, in %d byte chunks.\n",
348 			    special, bufrem, bufsize);
349 		while (bufrem > 0) {
350 			i = write(fso, buf, MIN(bufsize, bufrem));
351 			if (i == -1)
352 				err(1, "writing image");
353 			bufrem -= i;
354 		}
355 		free(buf);
356 	}
357 
358 	/* Sort out fragment and block sizes */
359 	if (bsize == 0) {
360 		bsize = fsize;
361 		if (bsize == 0) {
362 			if (fssize < SMALL_FSSIZE)
363 				bsize = S_DFL_BSIZE;
364 			else if (fssize < MEDIUM_FSSIZE)
365 				bsize = M_DFL_BSIZE;
366 			else
367 				bsize = L_DFL_BSIZE;
368 		}
369 	}
370 	if (fsize == 0)
371 		fsize = bsize;
372 
373 	blocks = fssize * sectorsize / bsize;
374 
375 	if (num_inodes == 0) {
376 		if (density != 0)
377 			num_inodes = fssize / density;
378 		else {
379 			if (fssize < SMALL_FSSIZE)
380 				num_inodes = S_DFL_NINODE(blocks);
381 			else if (fssize < MEDIUM_FSSIZE)
382 				num_inodes = M_DFL_NINODE(blocks);
383 			else
384 				num_inodes = L_DFL_NINODE(blocks);
385 		}
386 	}
387 	mke2fs(special, fsi, fso);
388 
389 	if (fsi != -1)
390 		close(fsi);
391 	if (fso != -1 && fso != fsi)
392 		close(fso);
393 	exit(EXIT_SUCCESS);
394 }
395 
396 static int64_t
397 strsuftoi64(const char *desc, const char *arg, int64_t min, int64_t max,
398     int *num_suffix)
399 {
400 	int64_t result, r1;
401 	int shift = 0;
402 	char *ep;
403 
404 	errno = 0;
405 	r1 = strtoll(arg, &ep, 10);
406 	if (ep[0] != '\0' && ep[1] != '\0')
407 		errx(EXIT_FAILURE,
408 		    "%s `%s' is not a valid number.", desc, arg);
409 	switch (ep[0]) {
410 	case '\0':
411 	case 's':
412 	case 'S':
413 		if (num_suffix != NULL)
414 			*num_suffix = 0;
415 		break;
416 	case 'g':
417 	case 'G':
418 		shift += 10;
419 		/* FALLTHROUGH */
420 	case 'm':
421 	case 'M':
422 		shift += 10;
423 		/* FALLTHROUGH */
424 	case 'k':
425 	case 'K':
426 		shift += 10;
427 		/* FALLTHROUGH */
428 	case 'b':
429 	case 'B':
430 		if (num_suffix != NULL)
431 			*num_suffix = 1;
432 		break;
433 	default:
434 		errx(EXIT_FAILURE,
435 		    "`%s' is not a valid suffix for %s.", ep, desc);
436 	}
437 	result = r1 << shift;
438 	if (errno == ERANGE || result >> shift != r1)
439 		errx(EXIT_FAILURE,
440 		    "%s `%s' is too large to convert.", desc, arg);
441 	if (result < min)
442 		errx(EXIT_FAILURE,
443 		    "%s `%s' (%" PRId64 ") is less than the minimum (%"
444 		    PRId64 ").", desc, arg, result, min);
445 	if (result > max)
446 		errx(EXIT_FAILURE,
447 		    "%s `%s' (%" PRId64 ") is greater than the maximum (%"
448 		    PRId64 ").", desc, arg, result, max);
449 	return result;
450 }
451 
452 static const char help_strings[] =
453 	"\t-b bsize\tblock size\n"
454 	"\t-D inodesize\tsize of an inode in bytes (128 or 256)\n"
455 	"\t-F \t\tcreate file system image in regular file\n"
456 	"\t-f fsize\tfragment size\n"
457 	"\t-I \t\tdo not check that the file system type is `Linux Ext2'\n"
458 	"\t-i density\tnumber of bytes per inode\n"
459 	"\t-m minfree\tminimum free space %\n"
460 	"\t-N \t\tdo not create file system, just print out parameters\n"
461 	"\t-n inodes\tnumber of inodes (overrides -i density)\n"
462 	"\t-O N\t\tfilesystem revision: 0 ==> REV0, 1 ==> REV1 (default 0)\n"
463 	"\t-S secsize\tsector size\n"
464 	"\t-s fssize\tfile system size (sectors)\n"
465 	"\t-V verbose\toutput verbosity: 0 ==> none, 4 ==> max\n"
466 	"\t-v volname\text2fs volume name\n"
467 	"\t-Z \t\tpre-zero the image file\n";
468 
469 static void
470 usage(void)
471 {
472 
473 	extern char *__progname;
474 
475 	fprintf(stderr,
476 	    "usage: %s [ fsoptions ] special-device\n", __progname);
477 	fprintf(stderr, "where fsoptions are:\n");
478 	fprintf(stderr, "%s", help_strings);
479 
480 	exit(EXIT_FAILURE);
481 }
482 
483 char lmsg[] = "%s: can't read disk label; disk type must be specified";
484 
485 struct disklabel *
486 getdisklabel(const char *s, int fd)
487 {
488 	static struct disklabel lab;
489 
490 	if (ioctl(fd, DIOCGDINFO, (char *)&lab) < 0) {
491 		if (disktype != NULL) {
492 			struct disklabel *lp;
493 
494 			//unlabeled++;
495 			lp = getdiskbyname(disktype);
496 			if (lp == NULL)
497 				errx(EXIT_FAILURE, "%s: unknown disk type",
498 				    disktype);
499 			return (lp);
500 		}
501 		warn("ioctl (GDINFO)");
502 		errx(EXIT_FAILURE, lmsg, s);
503 	}
504 	return (&lab);
505 }
506 
507 struct partition *
508 getpartition(int fsi, const char *special, char *argv[], struct disklabel **dl)
509 {
510 	struct stat st;
511 	const char *cp;
512 	struct disklabel *lp;
513 	struct partition *pp;
514 
515 	if (fstat(fsi, &st) < 0)
516 		errx(EXIT_FAILURE, "%s: %s", special, strerror(errno));
517 	if (S_ISBLK(st.st_mode))
518 		errx(EXIT_FAILURE, "%s: block device", special);
519 	if (!S_ISCHR(st.st_mode))
520 		warnx("%s: not a character-special device", special);
521 	cp = strchr(argv[0], '\0') - 1;
522 	if (cp == NULL || ((*cp < 'a' || *cp > ('a' + getmaxpartitions() - 1))
523 	    && !isdigit(*cp)))
524 		errx(EXIT_FAILURE, "%s: can't figure out file system partition", argv[0]);
525 	lp = getdisklabel(special, fsi);
526 	if (isdigit(*cp))
527 		pp = &lp->d_partitions[0];
528 	else
529 		pp = &lp->d_partitions[*cp - 'a'];
530 	if (DL_GETPSIZE(pp) == 0)
531 		errx(EXIT_FAILURE, "%s: `%c' partition is unavailable", argv[0], *cp);
532 	if (pp->p_fstype == FS_BOOT)
533 			errx(EXIT_FAILURE, "%s: `%c' partition overlaps boot program",
534 			      argv[0], *cp);
535 	*dl = lp;
536 	return pp;
537 }
538 
539