xref: /openbsd/sbin/newfs_ext2fs/newfs_ext2fs.c (revision d415bd75)
1 /* $OpenBSD: newfs_ext2fs.c,v 1.29 2022/12/04 23:50:47 cheloha 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 /*
34  * newfs: friendly front end to mke2fs
35  */
36 #include <sys/param.h>	/* powerof2 */
37 #include <sys/types.h>
38 #include <sys/ioctl.h>
39 #include <sys/dkio.h>
40 #include <sys/disklabel.h>
41 #include <sys/mount.h>
42 
43 #include <ufs/ext2fs/ext2fs.h>
44 #include <ufs/ext2fs/ext2fs_dinode.h>
45 
46 #include <ctype.h>
47 #include <err.h>
48 #include <errno.h>
49 #include <fcntl.h>
50 #include <inttypes.h>
51 #include <limits.h>
52 #include <paths.h>
53 #include <stdint.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <unistd.h>
58 #include <util.h>
59 
60 #define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
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 int	max_cols;
105 char	*volname = NULL;	/* volume name */
106 
107 static char *disktype = NULL;
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, fd, len, n, Fflag, Iflag, Zflag;
118 	char *s1, *s2, *special;
119 	const char *opstring;
120 	int byte_sized, fl;
121 	uint blocks;			/* number of blocks */
122 	struct partition *pp = NULL;
123 	struct disklabel *lp;
124 	struct winsize winsize;
125 
126 	/* Get terminal width */
127 	if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) == 0)
128 		max_cols = winsize.ws_col;
129 	else
130 		max_cols = 80;
131 
132 	if (pledge("stdio rpath wpath cpath disklabel", NULL) == -1)
133 		err(1, "pledge");
134 
135 	Fflag = Iflag = Zflag = 0;
136 	verbosity = -1;
137 	opstring = "D:FINO:S:V:Zb:f:i:l:m:n:qs:t:v:";
138 	byte_sized = 0;
139 	while ((ch = getopt(argc, argv, opstring)) != -1)
140 		switch (ch) {
141 		case 'D':
142 			inodesize = (uint16_t)strtol(optarg, &s1, 0);
143 			if (*s1 || (inodesize != 128 && inodesize != 256))
144 				errx(1, "Bad inode size %d "
145 				    "(only 128 and 256 supported)", inodesize);
146 			break;
147 		case 'F':
148 			Fflag = 1;
149 			break;
150 		case 'I':
151 			Iflag = 1;
152 			break;
153 		case 'N':
154 			Nflag = 1;
155 			if (verbosity == -1)
156 				verbosity = DEFAULT_VERBOSITY;
157 			break;
158 		case 'O':
159 			Oflag = strsuftoi64("format", optarg, 0, 1, NULL);
160 			break;
161 		case 'S':
162 			/*
163 			 * XXX:
164 			 * non-512 byte sectors almost certainly don't work.
165 			 */
166 			sectorsize = strsuftoi64("sector size",
167 			    optarg, 512, 65536, NULL);
168 			if (!powerof2(sectorsize))
169 				errx(EXIT_FAILURE,
170 				    "sector size `%s' is not a power of 2.",
171 				    optarg);
172 			break;
173 		case 'V':
174 			verbosity = strsuftoi64("verbose", optarg, 0, 4, NULL);
175 			break;
176 		case 'Z':
177 			Zflag = 1;
178 			break;
179 		case 'b':
180 			bsize = strsuftoi64("block size",
181 			    optarg, MINBSIZE, EXT2_MAXBSIZE, NULL);
182 			break;
183 		case 'f':
184 			fsize = strsuftoi64("fragment size",
185 			    optarg, MINBSIZE, EXT2_MAXBSIZE, NULL);
186 			break;
187 		case 'i':
188 			density = strsuftoi64("bytes per inode",
189 			    optarg, 1, INT_MAX, NULL);
190 			break;
191 		case 'm':
192 			minfree = strsuftoi64("free space %",
193 			    optarg, 0, 99, NULL);
194 			break;
195 		case 'n':
196 			num_inodes = strsuftoi64("number of inodes",
197 			    optarg, 1, INT_MAX, NULL);
198 			break;
199 		case 'q':
200 			verbosity = 1;
201 			break;
202 		case 's':
203 			fssize = strsuftoi64("file system size",
204 			    optarg, INT64_MIN, INT64_MAX, &byte_sized);
205 			break;
206 		case 't':
207 			/* compat with newfs -t */
208 			break;
209 		case 'v':
210 			volname = optarg;
211 			if (volname[0] == '\0')
212 				errx(EXIT_FAILURE,
213 				    "Volume name cannot be zero length");
214 			break;
215 		default:
216 			usage();
217 		}
218 	argc -= optind;
219 	argv += optind;
220 
221 	if (verbosity == -1)
222 		/* Default to showing cg info */
223 		verbosity = DEFAULT_VERBOSITY;
224 
225 	if (argc != 1)
226 		usage();
227 
228 	memset(&sb, 0, sizeof(sb));
229 	special = argv[0];
230 	fl = Nflag ? O_RDONLY : O_RDWR;
231 
232 	if (Fflag) {
233 		/*
234 		 * It's a file system image
235 		 * no label, use fixed default for sectorsize.
236 		 */
237 		if (sectorsize == 0)
238 			sectorsize = DFL_SECSIZE;
239 
240 		/* creating image in a regular file */
241 		if (!Nflag && fssize > 0)
242 			fl |= O_CREAT;
243 		fd = open(special, fl, 0666);
244 		if (fd == -1)
245 			err(EXIT_FAILURE, "can't open file %s", special);
246 		if (fstat(fd, &sb) == -1)
247 			err(EXIT_FAILURE, "can't fstat opened %s", special);
248 	} else {	/* !Fflag */
249 		fd = opendev(special, fl, 0, &special);
250 		if (fd == -1 || fstat(fd, &sb) == -1)
251 			err(EXIT_FAILURE, "%s: open", special);
252 
253 		if (!Nflag) {
254 			/* Bail if target special is mounted */
255 			n = getmntinfo(&mp, MNT_NOWAIT);
256 			if (n == 0)
257 				err(EXIT_FAILURE, "%s: getmntinfo", special);
258 
259 			len = sizeof(_PATH_DEV) - 1;
260 			s1 = special;
261 			if (strncmp(_PATH_DEV, s1, len) == 0)
262 				s1 += len;
263 
264 			while (--n >= 0) {
265 				s2 = mp->f_mntfromname;
266 				if (strncmp(_PATH_DEV, s2, len) == 0) {
267 					s2 += len - 1;
268 					*s2 = 'r';
269 				}
270 				if (strcmp(s1, s2) == 0 ||
271 				    strcmp(s1, &s2[1]) == 0)
272 					errx(EXIT_FAILURE,
273 					    "%s is mounted on %s",
274 					    special, mp->f_mntonname);
275 				++mp;
276 			}
277 		}
278 
279 		pp = getpartition(fd, special, argv, &lp);
280 		if (!Iflag) {
281 			static const char m[] =
282 			    "%s partition type is not `%s' (or use -I)";
283 			if (pp->p_fstype != FS_EXT2FS)
284 				errx(EXIT_FAILURE, m, special, "ext2fs");
285 		}
286 		if (sectorsize == 0) {
287 			sectorsize = lp->d_secsize;
288 			if (sectorsize <= 0)
289 				errx(EXIT_FAILURE, "no default sector size");
290 		}
291 	}
292 
293 	if (byte_sized)
294 		fssize /= sectorsize;
295 	if (fssize <= 0) {
296 		if (sb.st_size != 0)
297 			fssize += sb.st_size / sectorsize;
298 		else if (pp)
299 			fssize += DL_GETPSIZE(pp);
300 		if (fssize <= 0)
301 			errx(EXIT_FAILURE,
302 			    "Unable to determine file system size");
303 	}
304 
305 	/* XXXLUKEM: only ftruncate() regular files ? (dsl: or at all?) */
306 	if (Fflag && !Nflag
307 	    && ftruncate(fd, (off_t)fssize * sectorsize) == -1)
308 		err(1, "can't ftruncate %s to %" PRId64, special, fssize);
309 
310 	if (Zflag && !Nflag) {	/* pre-zero (and de-sparce) the file */
311 		char *buf;
312 		int bufsize, i;
313 		off_t bufrem;
314 		struct statfs sfs;
315 
316 		if (fstatfs(fd, &sfs) == -1) {
317 			warn("can't fstatvfs `%s'", special);
318 			bufsize = 8192;
319 		} else
320 			bufsize = sfs.f_iosize;
321 
322 		if ((buf = calloc(1, bufsize)) == NULL)
323 			err(1, "can't allocate buffer of %d",
324 			bufsize);
325 		bufrem = fssize * sectorsize;
326 		if (verbosity > 0)
327 			printf("Creating file system image in `%s', "
328 			    "size %" PRId64 " bytes, in %d byte chunks.\n",
329 			    special, bufrem, bufsize);
330 		while (bufrem > 0) {
331 			i = write(fd, buf, MINIMUM(bufsize, bufrem));
332 			if (i == -1)
333 				err(1, "writing image");
334 			bufrem -= i;
335 		}
336 		free(buf);
337 	}
338 
339 	/* Sort out fragment and block sizes */
340 	if (bsize == 0) {
341 		bsize = fsize;
342 		if (bsize == 0) {
343 			if (fssize < SMALL_FSSIZE)
344 				bsize = S_DFL_BSIZE;
345 			else if (fssize < MEDIUM_FSSIZE)
346 				bsize = M_DFL_BSIZE;
347 			else
348 				bsize = L_DFL_BSIZE;
349 		}
350 	}
351 	if (fsize == 0)
352 		fsize = bsize;
353 
354 	blocks = fssize * sectorsize / bsize;
355 
356 	if (num_inodes == 0) {
357 		if (density != 0)
358 			num_inodes = fssize / density;
359 		else {
360 			if (fssize < SMALL_FSSIZE)
361 				num_inodes = S_DFL_NINODE(blocks);
362 			else if (fssize < MEDIUM_FSSIZE)
363 				num_inodes = M_DFL_NINODE(blocks);
364 			else
365 				num_inodes = L_DFL_NINODE(blocks);
366 		}
367 	}
368 	mke2fs(special, fd);
369 
370 	close(fd);
371 	exit(EXIT_SUCCESS);
372 }
373 
374 static int64_t
375 strsuftoi64(const char *desc, const char *arg, int64_t min, int64_t max,
376     int *num_suffix)
377 {
378 	int64_t result, r1;
379 	int shift = 0;
380 	char *ep;
381 
382 	errno = 0;
383 	r1 = strtoll(arg, &ep, 10);
384 	if (ep[0] != '\0' && ep[1] != '\0')
385 		errx(EXIT_FAILURE,
386 		    "%s `%s' is not a valid number.", desc, arg);
387 	switch (ep[0]) {
388 	case '\0':
389 	case 's':
390 	case 'S':
391 		if (num_suffix != NULL)
392 			*num_suffix = 0;
393 		break;
394 	case 'g':
395 	case 'G':
396 		shift += 10;
397 		/* FALLTHROUGH */
398 	case 'm':
399 	case 'M':
400 		shift += 10;
401 		/* FALLTHROUGH */
402 	case 'k':
403 	case 'K':
404 		shift += 10;
405 		/* FALLTHROUGH */
406 	case 'b':
407 	case 'B':
408 		if (num_suffix != NULL)
409 			*num_suffix = 1;
410 		break;
411 	default:
412 		errx(EXIT_FAILURE,
413 		    "`%s' is not a valid suffix for %s.", ep, desc);
414 	}
415 	result = r1 << shift;
416 	if (errno == ERANGE || result >> shift != r1)
417 		errx(EXIT_FAILURE,
418 		    "%s `%s' is too large to convert.", desc, arg);
419 	if (result < min)
420 		errx(EXIT_FAILURE,
421 		    "%s `%s' (%" PRId64 ") is less than the minimum (%"
422 		    PRId64 ").", desc, arg, result, min);
423 	if (result > max)
424 		errx(EXIT_FAILURE,
425 		    "%s `%s' (%" PRId64 ") is greater than the maximum (%"
426 		    PRId64 ").", desc, arg, result, max);
427 	return result;
428 }
429 
430 static void
431 usage(void)
432 {
433 	extern char *__progname;
434 
435 	fprintf(stderr,
436 	    "usage: %s [ fsoptions ] special-device\n", __progname);
437 	exit(EXIT_FAILURE);
438 }
439 
440 struct disklabel *
441 getdisklabel(const char *s, int fd)
442 {
443 	static struct disklabel lab;
444 
445 	if (ioctl(fd, DIOCGDINFO, (char *)&lab) == -1) {
446 		if (disktype != NULL) {
447 			struct disklabel *lp;
448 
449 			//unlabeled++;
450 			lp = getdiskbyname(disktype);
451 			if (lp == NULL)
452 				errx(EXIT_FAILURE, "%s: unknown disk type",
453 				    disktype);
454 			return (lp);
455 		}
456 		warn("ioctl (GDINFO)");
457 		errx(EXIT_FAILURE,
458 		    "%s: can't read disk label; disk type must be specified",
459 		    s);
460 	}
461 	return (&lab);
462 }
463 
464 struct partition *
465 getpartition(int fsi, const char *special, char *argv[], struct disklabel **dl)
466 {
467 	struct stat st;
468 	const char *cp;
469 	struct disklabel *lp;
470 	struct partition *pp;
471 
472 	if (fstat(fsi, &st) == -1)
473 		err(EXIT_FAILURE, "%s", special);
474 	if (S_ISBLK(st.st_mode))
475 		errx(EXIT_FAILURE, "%s: block device", special);
476 	if (!S_ISCHR(st.st_mode))
477 		warnx("%s: not a character-special device", special);
478 	if (*argv[0] == '\0')
479 		errx(EXIT_FAILURE, "empty partition name supplied");
480 	cp = argv[0] + strlen(argv[0]) - 1;
481 	if ((*cp < 'a' || *cp > ('a' + getmaxpartitions() - 1))
482 	    && !isdigit((unsigned char)*cp))
483 		errx(EXIT_FAILURE, "%s: can't figure out file system partition", argv[0]);
484 	lp = getdisklabel(special, fsi);
485 	if (isdigit((unsigned char)*cp))
486 		pp = &lp->d_partitions[0];
487 	else
488 		pp = &lp->d_partitions[*cp - 'a'];
489 	if (DL_GETPSIZE(pp) == 0)
490 		errx(EXIT_FAILURE, "%s: `%c' partition is unavailable", argv[0], *cp);
491 	*dl = lp;
492 	return pp;
493 }
494 
495