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
main(int argc,char * argv[])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
strsuftoi64(const char * desc,const char * arg,int64_t min,int64_t max,int * num_suffix)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
usage(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 *
getdisklabel(const char * s,int fd)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 *
getpartition(int fsi,const char * special,char * argv[],struct disklabel ** dl)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