1 /* $OpenBSD: newfs.c,v 1.108 2016/07/25 15:30:36 krw Exp $ */ 2 /* $NetBSD: newfs.c,v 1.20 1996/05/16 07:13:03 thorpej Exp $ */ 3 4 /* 5 * Copyright (c) 2002 Networks Associates Technology, Inc. 6 * All rights reserved. 7 * 8 * This software was developed for the FreeBSD Project by Marshall 9 * Kirk McKusick and Network Associates Laboratories, the Security 10 * Research Division of Network Associates, Inc. under DARPA/SPAWAR 11 * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS 12 * research program. 13 * 14 * Copyright (c) 1983, 1989, 1993, 1994 15 * The Regents of the University of California. All rights reserved. 16 * 17 * Redistribution and use in source and binary forms, with or without 18 * modification, are permitted provided that the following conditions 19 * are met: 20 * 1. Redistributions of source code must retain the above copyright 21 * notice, this list of conditions and the following disclaimer. 22 * 2. Redistributions in binary form must reproduce the above copyright 23 * notice, this list of conditions and the following disclaimer in the 24 * documentation and/or other materials provided with the distribution. 25 * 3. Neither the name of the University nor the names of its contributors 26 * may be used to endorse or promote products derived from this software 27 * without specific prior written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 39 * SUCH DAMAGE. 40 */ 41 42 #include <sys/param.h> /* DEV_BSIZE MAXBSIZE */ 43 #include <sys/types.h> 44 #include <sys/stat.h> 45 #include <sys/ioctl.h> 46 #include <sys/dkio.h> 47 #include <sys/disklabel.h> 48 #include <sys/mount.h> 49 #include <sys/resource.h> 50 #include <sys/sysctl.h> 51 #include <sys/wait.h> 52 53 #include <ufs/ufs/dinode.h> 54 #include <ufs/ufs/dir.h> 55 #include <ufs/ffs/fs.h> 56 57 #include <ctype.h> 58 #include <err.h> 59 #include <errno.h> 60 #include <fcntl.h> 61 #include <paths.h> 62 #include <stdarg.h> 63 #include <stdio.h> 64 #include <stdlib.h> 65 #include <string.h> 66 #include <syslog.h> 67 #include <unistd.h> 68 #include <limits.h> 69 #include <signal.h> 70 #include <util.h> 71 72 #include "mntopts.h" 73 #include "pathnames.h" 74 75 #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) 76 #define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) 77 78 struct mntopt mopts[] = { 79 MOPT_STDOPTS, 80 MOPT_WXALLOWED, 81 MOPT_ASYNC, 82 MOPT_UPDATE, 83 MOPT_FORCE, 84 { NULL }, 85 }; 86 87 void fatal(const char *fmt, ...) 88 __attribute__((__format__ (printf, 1, 2))) 89 __attribute__((__nonnull__ (1))); 90 __dead void usage(void); 91 void mkfs(struct partition *, char *, int, int, mode_t, uid_t, gid_t); 92 void getphysmem(void); 93 void rewritelabel(char *, int, struct disklabel *); 94 u_short dkcksum(struct disklabel *); 95 96 /* 97 * The following two constants set the default block and fragment sizes. 98 * Both constants must be a power of 2 and meet the following constraints: 99 * MINBSIZE <= DESBLKSIZE <= MAXBSIZE 100 * sectorsize <= DESFRAGSIZE <= DESBLKSIZE 101 * DESBLKSIZE / DESFRAGSIZE <= 8 102 */ 103 #define DFL_FRAGSIZE 2048 104 #define DFL_BLKSIZE 16384 105 106 /* 107 * MAXBLKPG determines the maximum number of data blocks which are 108 * placed in a single cylinder group. The default is one indirect 109 * block worth of data blocks. 110 */ 111 #define MAXBLKPG_FFS1(bsize) ((bsize) / sizeof(int32_t)) 112 #define MAXBLKPG_FFS2(bsize) ((bsize) / sizeof(int64_t)) 113 114 /* 115 * Each file system has a number of inodes statically allocated. 116 * We allocate one inode slot per NFPI fragments, expecting this 117 * to be far more than we will ever need. 118 */ 119 #define NFPI 4 120 121 int mfs; /* run as the memory based filesystem */ 122 int Nflag; /* run without writing file system */ 123 int Oflag = 1; /* 0 = 4.3BSD ffs, 1 = 4.4BSD ffs, 2 = ffs2 */ 124 daddr_t fssize; /* file system size in 512-byte blocks */ 125 long long sectorsize; /* bytes/sector */ 126 int fsize = 0; /* fragment size */ 127 int bsize = 0; /* block size */ 128 int maxfrgspercg = INT_MAX; /* maximum fragments per cylinder group */ 129 int minfree = MINFREE; /* free space threshold */ 130 int opt = DEFAULTOPT; /* optimization preference (space or time) */ 131 int reqopt = -1; /* opt preference has not been specified */ 132 int density; /* number of bytes per inode */ 133 int maxbpg; /* maximum blocks per file in a cyl group */ 134 int avgfilesize = AVFILESIZ;/* expected average file size */ 135 int avgfilesperdir = AFPDIR;/* expected number of files per directory */ 136 int mntflags = MNT_ASYNC; /* flags to be passed to mount */ 137 int quiet = 0; /* quiet flag */ 138 caddr_t membase; /* start address of memory based filesystem */ 139 char *disktype; 140 int unlabeled; 141 142 extern char *__progname; 143 struct disklabel *getdisklabel(char *, int); 144 145 #ifdef MFS 146 static int do_exec(const char *, const char *, char *const[]); 147 static int isdir(const char *); 148 static void copy(char *, char *, struct mfs_args *); 149 static int gettmpmnt(char *, size_t); 150 #endif 151 152 int64_t physmem; 153 154 void 155 getphysmem(void) 156 { 157 int mib[] = { CTL_HW, HW_PHYSMEM64 }; 158 size_t len = sizeof(physmem); 159 160 if (sysctl(mib, 2, &physmem, &len, NULL, 0) != 0) 161 err(1, "can't get physmem"); 162 } 163 164 int 165 main(int argc, char *argv[]) 166 { 167 int ch; 168 struct partition *pp; 169 struct disklabel *lp; 170 struct disklabel mfsfakelabel; 171 struct partition oldpartition; 172 struct stat st; 173 struct statfs *mp; 174 struct rlimit rl; 175 int fsi = -1, oflagset = 0, fso, len, n, maxpartitions; 176 char *cp = NULL, *s1, *s2, *special, *opstring, *realdev; 177 #ifdef MFS 178 char mountfromname[BUFSIZ]; 179 char *pop = NULL, node[PATH_MAX]; 180 pid_t pid, res; 181 struct statfs sf; 182 struct stat mountpoint; 183 int status; 184 #endif 185 uid_t mfsuid = 0; 186 gid_t mfsgid = 0; 187 mode_t mfsmode = 0; 188 char *fstype = NULL; 189 char **saveargv = argv; 190 int ffsflag = 1; 191 const char *errstr; 192 long long fssize_input = 0; 193 int fssize_usebytes = 0; 194 u_int64_t nsecs; 195 196 if (strstr(__progname, "mfs")) 197 mfs = Nflag = quiet = 1; 198 199 getphysmem(); 200 maxpartitions = getmaxpartitions(); 201 if (maxpartitions > 26) 202 fatal("insane maxpartitions value %d", maxpartitions); 203 204 opstring = mfs ? 205 "P:T:b:c:e:f:i:m:o:s:" : 206 "NO:S:T:b:c:e:f:g:h:i:m:o:qs:t:"; 207 while ((ch = getopt(argc, argv, opstring)) != -1) { 208 switch (ch) { 209 case 'N': 210 Nflag = 1; 211 break; 212 case 'O': 213 Oflag = strtonum(optarg, 0, 2, &errstr); 214 if (errstr) 215 fatal("%s: invalid ffs version", optarg); 216 oflagset = 1; 217 break; 218 case 'S': 219 if (scan_scaled(optarg, §orsize) == -1 || 220 sectorsize <= 0 || (sectorsize % DEV_BSIZE)) 221 fatal("sector size invalid: %s", optarg); 222 break; 223 case 'T': 224 disktype = optarg; 225 break; 226 case 'b': 227 bsize = strtonum(optarg, MINBSIZE, MAXBSIZE, &errstr); 228 if (errstr) 229 fatal("block size is %s: %s", errstr, optarg); 230 break; 231 case 'c': 232 maxfrgspercg = strtonum(optarg, 1, INT_MAX, &errstr); 233 if (errstr) 234 fatal("fragments per cylinder group is %s: %s", 235 errstr, optarg); 236 break; 237 case 'e': 238 maxbpg = strtonum(optarg, 1, INT_MAX, &errstr); 239 if (errstr) 240 fatal("blocks per file in a cylinder group is" 241 " %s: %s", errstr, optarg); 242 break; 243 case 'f': 244 fsize = strtonum(optarg, MINBSIZE / MAXFRAG, MAXBSIZE, 245 &errstr); 246 if (errstr) 247 fatal("fragment size is %s: %s", 248 errstr, optarg); 249 break; 250 case 'g': 251 avgfilesize = strtonum(optarg, 1, INT_MAX, &errstr); 252 if (errstr) 253 fatal("average file size is %s: %s", 254 errstr, optarg); 255 break; 256 case 'h': 257 avgfilesperdir = strtonum(optarg, 1, INT_MAX, &errstr); 258 if (errstr) 259 fatal("average files per dir is %s: %s", 260 errstr, optarg); 261 break; 262 case 'i': 263 density = strtonum(optarg, 1, INT_MAX, &errstr); 264 if (errstr) 265 fatal("bytes per inode is %s: %s", 266 errstr, optarg); 267 break; 268 case 'm': 269 minfree = strtonum(optarg, 0, 99, &errstr); 270 if (errstr) 271 fatal("free space %% is %s: %s", 272 errstr, optarg); 273 break; 274 case 'o': 275 if (mfs) 276 getmntopts(optarg, mopts, &mntflags); 277 else { 278 if (strcmp(optarg, "space") == 0) 279 reqopt = opt = FS_OPTSPACE; 280 else if (strcmp(optarg, "time") == 0) 281 reqopt = opt = FS_OPTTIME; 282 else 283 fatal("%s: unknown optimization " 284 "preference: use `space' or `time'.", 285 optarg); 286 } 287 break; 288 case 'q': 289 quiet = 1; 290 break; 291 case 's': 292 if (scan_scaled(optarg, &fssize_input) == -1 || 293 fssize_input <= 0) 294 fatal("file system size invalid: %s", optarg); 295 fssize_usebytes = 0; /* in case of multiple -s */ 296 for (s1 = optarg; *s1 != '\0'; s1++) 297 if (isalpha((unsigned char)*s1)) { 298 fssize_usebytes = 1; 299 break; 300 } 301 break; 302 case 't': 303 fstype = optarg; 304 if (strcmp(fstype, "ffs")) 305 ffsflag = 0; 306 break; 307 #ifdef MFS 308 case 'P': 309 pop = optarg; 310 break; 311 #endif 312 case '?': 313 default: 314 usage(); 315 } 316 if (!ffsflag) 317 break; 318 } 319 argc -= optind; 320 argv += optind; 321 322 if (ffsflag && argc - mfs != 1) 323 usage(); 324 325 if (mfs) { 326 /* Increase our data size to the max */ 327 if (getrlimit(RLIMIT_DATA, &rl) == 0) { 328 rl.rlim_cur = rl.rlim_max; 329 (void)setrlimit(RLIMIT_DATA, &rl); 330 } 331 } 332 333 special = argv[0]; 334 335 if (!mfs) { 336 char execname[PATH_MAX], name[PATH_MAX]; 337 338 if (fstype == NULL) 339 fstype = readlabelfs(special, 0); 340 if (fstype != NULL && strcmp(fstype, "ffs")) { 341 snprintf(name, sizeof name, "newfs_%s", fstype); 342 saveargv[0] = name; 343 snprintf(execname, sizeof execname, "%s/newfs_%s", 344 _PATH_SBIN, fstype); 345 (void)execv(execname, saveargv); 346 snprintf(execname, sizeof execname, "%s/newfs_%s", 347 _PATH_USRSBIN, fstype); 348 (void)execv(execname, saveargv); 349 err(1, "%s not found", name); 350 } 351 } 352 353 if (mfs && !strcmp(special, "swap")) { 354 /* 355 * it's an MFS, mounted on "swap." fake up a label. 356 * XXX XXX XXX 357 */ 358 fso = -1; /* XXX; normally done below. */ 359 360 memset(&mfsfakelabel, 0, sizeof(mfsfakelabel)); 361 mfsfakelabel.d_secsize = 512; 362 mfsfakelabel.d_nsectors = 64; 363 mfsfakelabel.d_ntracks = 16; 364 mfsfakelabel.d_ncylinders = 16; 365 mfsfakelabel.d_secpercyl = 1024; 366 DL_SETDSIZE(&mfsfakelabel, 16384); 367 mfsfakelabel.d_npartitions = 1; 368 mfsfakelabel.d_version = 1; 369 DL_SETPSIZE(&mfsfakelabel.d_partitions[0], 16384); 370 mfsfakelabel.d_partitions[0].p_fragblock = 371 DISKLABELV1_FFS_FRAGBLOCK(1024, 8); 372 mfsfakelabel.d_partitions[0].p_cpg = 16; 373 374 lp = &mfsfakelabel; 375 pp = &mfsfakelabel.d_partitions[0]; 376 377 goto havelabel; 378 } 379 if (Nflag) { 380 fso = -1; 381 } else { 382 fso = opendev(special, O_WRONLY, 0, &realdev); 383 if (fso < 0) 384 fatal("%s: %s", special, strerror(errno)); 385 special = realdev; 386 387 /* Bail if target special is mounted */ 388 n = getmntinfo(&mp, MNT_NOWAIT); 389 if (n == 0) 390 fatal("%s: getmntinfo: %s", special, strerror(errno)); 391 392 len = sizeof(_PATH_DEV) - 1; 393 s1 = special; 394 if (strncmp(_PATH_DEV, s1, len) == 0) 395 s1 += len; 396 397 while (--n >= 0) { 398 s2 = mp->f_mntfromname; 399 if (strncmp(_PATH_DEV, s2, len) == 0) { 400 s2 += len - 1; 401 *s2 = 'r'; 402 } 403 if (strcmp(s1, s2) == 0 || strcmp(s1, &s2[1]) == 0) 404 fatal("%s is mounted on %s", 405 special, mp->f_mntonname); 406 ++mp; 407 } 408 } 409 if (mfs && disktype != NULL) { 410 lp = (struct disklabel *)getdiskbyname(disktype); 411 if (lp == NULL) 412 fatal("%s: unknown disk type", disktype); 413 pp = &lp->d_partitions[1]; 414 } else { 415 fsi = opendev(special, O_RDONLY, 0, NULL); 416 if (fsi < 0) 417 fatal("%s: %s", special, strerror(errno)); 418 if (fstat(fsi, &st) < 0) 419 fatal("%s: %s", special, strerror(errno)); 420 if (!mfs) { 421 if (S_ISBLK(st.st_mode)) 422 fatal("%s: block device", special); 423 if (!S_ISCHR(st.st_mode)) 424 warnx("%s: not a character-special device", 425 special); 426 } 427 if (*argv[0] == '\0') 428 fatal("empty partition name supplied"); 429 cp = argv[0] + strlen(argv[0]) - 1; 430 if ((*cp < 'a' || *cp > ('a' + maxpartitions - 1)) 431 && !isdigit((unsigned char)*cp)) 432 fatal("%s: can't figure out file system partition", 433 argv[0]); 434 lp = getdisklabel(special, fsi); 435 if (!mfs) { 436 if (pledge("stdio disklabel tty", NULL) == -1) 437 err(1, "pledge"); 438 } 439 if (isdigit((unsigned char)*cp)) 440 pp = &lp->d_partitions[0]; 441 else 442 pp = &lp->d_partitions[*cp - 'a']; 443 if (DL_GETPSIZE(pp) == 0) 444 fatal("%s: `%c' partition is unavailable", 445 argv[0], *cp); 446 if (pp->p_fstype == FS_BOOT) 447 fatal("%s: `%c' partition overlaps boot program", 448 argv[0], *cp); 449 } 450 havelabel: 451 if (sectorsize == 0) { 452 sectorsize = lp->d_secsize; 453 if (sectorsize <= 0) 454 fatal("%s: no default sector size", argv[0]); 455 } 456 457 if (fssize_usebytes) { 458 nsecs = fssize_input / sectorsize; 459 if (fssize_input % sectorsize != 0) 460 nsecs++; 461 } else if (fssize_input == 0) 462 nsecs = DL_GETPSIZE(pp); 463 else 464 nsecs = fssize_input; 465 466 if (nsecs > DL_GETPSIZE(pp) && !mfs) 467 fatal("%s: maximum file system size on the `%c' partition is " 468 "%llu sectors", argv[0], *cp, DL_GETPSIZE(pp)); 469 470 /* Can't use DL_SECTOBLK() because sectorsize may not be from label! */ 471 fssize = nsecs * (sectorsize / DEV_BSIZE); 472 if (oflagset == 0 && fssize >= INT_MAX) 473 Oflag = 2; /* FFS2 */ 474 if (fsize == 0) { 475 fsize = DISKLABELV1_FFS_FSIZE(pp->p_fragblock); 476 if (fsize <= 0) 477 fsize = MAXIMUM(DFL_FRAGSIZE, lp->d_secsize); 478 } 479 if (bsize == 0) { 480 bsize = DISKLABELV1_FFS_BSIZE(pp->p_fragblock); 481 if (bsize <= 0) 482 bsize = MINIMUM(DFL_BLKSIZE, 8 * fsize); 483 } 484 if (density == 0) 485 density = NFPI * fsize; 486 if (minfree < MINFREE && opt != FS_OPTSPACE && reqopt == -1) { 487 warnx("warning: changing optimization to space " 488 "because minfree is less than %d%%\n", MINFREE); 489 opt = FS_OPTSPACE; 490 } 491 if (maxbpg == 0) { 492 if (Oflag <= 1) 493 maxbpg = MAXBLKPG_FFS1(bsize); 494 else 495 maxbpg = MAXBLKPG_FFS2(bsize); 496 } 497 oldpartition = *pp; 498 #ifdef MFS 499 if (mfs) { 500 if (realpath(argv[1], node) == NULL) 501 err(1, "realpath %s", argv[1]); 502 if (stat(node, &mountpoint) < 0) 503 err(ECANCELED, "stat %s", node); 504 mfsuid = mountpoint.st_uid; 505 mfsgid = mountpoint.st_gid; 506 mfsmode = mountpoint.st_mode & ALLPERMS; 507 } 508 #endif 509 510 mkfs(pp, special, fsi, fso, mfsmode, mfsuid, mfsgid); 511 if (!Nflag && memcmp(pp, &oldpartition, sizeof(oldpartition))) 512 rewritelabel(special, fso, lp); 513 if (!Nflag) 514 close(fso); 515 close(fsi); 516 #ifdef MFS 517 if (mfs) { 518 struct mfs_args args; 519 memset(&args, 0, sizeof(args)); 520 args.base = membase; 521 args.size = fssize * DEV_BSIZE; 522 args.export_info.ex_root = -2; 523 if (mntflags & MNT_RDONLY) 524 args.export_info.ex_flags = MNT_EXRDONLY; 525 526 switch (pid = fork()) { 527 case -1: 528 err(10, "mfs"); 529 case 0: 530 snprintf(mountfromname, sizeof(mountfromname), 531 "mfs:%d", getpid()); 532 break; 533 default: 534 snprintf(mountfromname, sizeof(mountfromname), 535 "mfs:%d", pid); 536 for (;;) { 537 /* 538 * spin until the mount succeeds 539 * or the child exits 540 */ 541 usleep(1); 542 543 /* 544 * XXX Here is a race condition: another process 545 * can mount a filesystem which hides our 546 * ramdisk before we see the success. 547 */ 548 if (statfs(node, &sf) < 0) 549 err(ECANCELED, "statfs %s", node); 550 if (!strcmp(sf.f_mntfromname, mountfromname) && 551 !strncmp(sf.f_mntonname, node, 552 MNAMELEN) && 553 !strcmp(sf.f_fstypename, "mfs")) { 554 if (pop != NULL) 555 copy(pop, node, &args); 556 exit(0); 557 } 558 res = waitpid(pid, &status, WNOHANG); 559 if (res == -1) 560 err(EDEADLK, "waitpid"); 561 if (res != pid) 562 continue; 563 if (WIFEXITED(status)) { 564 if (WEXITSTATUS(status) == 0) 565 exit(0); 566 errx(1, "%s: mount: %s", node, 567 strerror(WEXITSTATUS(status))); 568 } else 569 errx(EDEADLK, "abnormal termination"); 570 } 571 /* NOTREACHED */ 572 } 573 574 (void) setsid(); 575 (void) close(0); 576 (void) close(1); 577 (void) close(2); 578 (void) chdir("/"); 579 580 args.fspec = mountfromname; 581 if (mntflags & MNT_RDONLY && pop != NULL) 582 mntflags &= ~MNT_RDONLY; 583 if (mount(MOUNT_MFS, node, mntflags, &args) < 0) 584 exit(errno); /* parent prints message */ 585 } 586 #endif 587 exit(0); 588 } 589 590 struct disklabel * 591 getdisklabel(char *s, int fd) 592 { 593 static struct disklabel lab; 594 595 if (ioctl(fd, DIOCGDINFO, (char *)&lab) < 0) { 596 if (disktype != NULL) { 597 struct disklabel *lp; 598 599 unlabeled++; 600 lp = getdiskbyname(disktype); 601 if (lp == NULL) 602 fatal("%s: unknown disk type", disktype); 603 return (lp); 604 } 605 warn("ioctl (GDINFO)"); 606 fatal("%s: can't read disk label; disk type must be specified", 607 s); 608 } 609 return (&lab); 610 } 611 612 void 613 rewritelabel(char *s, int fd, struct disklabel *lp) 614 { 615 if (unlabeled) 616 return; 617 618 lp->d_checksum = 0; 619 lp->d_checksum = dkcksum(lp); 620 if (ioctl(fd, DIOCWDINFO, (char *)lp) < 0) { 621 warn("ioctl (WDINFO)"); 622 fatal("%s: can't rewrite disk label", s); 623 } 624 } 625 626 void 627 fatal(const char *fmt, ...) 628 { 629 va_list ap; 630 631 va_start(ap, fmt); 632 if (fcntl(STDERR_FILENO, F_GETFL) < 0) { 633 openlog(__progname, LOG_CONS, LOG_DAEMON); 634 vsyslog(LOG_ERR, fmt, ap); 635 closelog(); 636 } else { 637 vwarnx(fmt, ap); 638 } 639 va_end(ap); 640 exit(1); 641 /*NOTREACHED*/ 642 } 643 644 __dead void 645 usage(void) 646 { 647 extern char *__progname; 648 649 if (mfs) { 650 fprintf(stderr, 651 "usage: %s [-b block-size] [-c fragments-per-cylinder-group] " 652 "[-e maxbpg]\n" 653 "\t[-f frag-size] [-i bytes] [-m free-space] [-o options] " 654 "[-P file]\n" 655 "\t[-s size] special node\n", 656 __progname); 657 } else { 658 fprintf(stderr, 659 "usage: %s [-Nq] [-b block-size] " 660 "[-c fragments-per-cylinder-group] [-e maxbpg]\n" 661 "\t[-f frag-size] [-g avgfilesize] [-h avgfpdir] [-i bytes]\n" 662 "\t[-m free-space] [-O filesystem-format] [-o optimization]\n" 663 "\t[-S sector-size] [-s size] [-T disktype] [-t fstype] " 664 "special\n", 665 __progname); 666 } 667 668 exit(1); 669 } 670 671 #ifdef MFS 672 673 static int 674 do_exec(const char *dir, const char *cmd, char *const argv[]) 675 { 676 pid_t pid; 677 int ret, status; 678 sig_t intsave, quitsave; 679 680 switch (pid = fork()) { 681 case -1: 682 err(1, "fork"); 683 case 0: 684 if (dir != NULL && chdir(dir) != 0) 685 err(1, "chdir"); 686 if (execv(cmd, argv) != 0) 687 err(1, "%s", cmd); 688 break; 689 default: 690 intsave = signal(SIGINT, SIG_IGN); 691 quitsave = signal(SIGQUIT, SIG_IGN); 692 for (;;) { 693 ret = waitpid(pid, &status, 0); 694 if (ret == -1) 695 err(11, "waitpid"); 696 if (WIFEXITED(status)) { 697 status = WEXITSTATUS(status); 698 if (status != 0) 699 warnx("%s: exited", cmd); 700 break; 701 } else if (WIFSIGNALED(status)) { 702 warnx("%s: %s", cmd, 703 strsignal(WTERMSIG(status))); 704 status = 1; 705 break; 706 } 707 } 708 signal(SIGINT, intsave); 709 signal(SIGQUIT, quitsave); 710 return (status); 711 } 712 /* NOTREACHED */ 713 return (-1); 714 } 715 716 static int 717 isdir(const char *path) 718 { 719 struct stat st; 720 721 if (stat(path, &st) != 0) 722 err(1, "cannot stat %s", path); 723 if (!S_ISDIR(st.st_mode) && !S_ISBLK(st.st_mode)) 724 errx(1, "%s: not a dir or a block device", path); 725 return (S_ISDIR(st.st_mode)); 726 } 727 728 static void 729 copy(char *src, char *dst, struct mfs_args *args) 730 { 731 int ret, dir, created = 0; 732 struct ufs_args mount_args; 733 char mountpoint[MNAMELEN]; 734 char *const argv[] = { "pax", "-rw", "-pe", ".", dst, NULL } ; 735 736 dir = isdir(src); 737 if (dir) 738 strlcpy(mountpoint, src, sizeof(mountpoint)); 739 else { 740 created = gettmpmnt(mountpoint, sizeof(mountpoint)); 741 memset(&mount_args, 0, sizeof(mount_args)); 742 mount_args.fspec = src; 743 ret = mount(MOUNT_FFS, mountpoint, MNT_RDONLY, &mount_args); 744 if (ret != 0) { 745 int saved_errno = errno; 746 if (created && rmdir(mountpoint) != 0) 747 warn("rmdir %s", mountpoint); 748 if (unmount(dst, 0) != 0) 749 warn("unmount %s", dst); 750 errc(1, saved_errno, "mount %s %s", src, mountpoint); 751 } 752 } 753 ret = do_exec(mountpoint, "/bin/pax", argv); 754 if (!dir && unmount(mountpoint, 0) != 0) 755 warn("unmount %s", mountpoint); 756 if (created && rmdir(mountpoint) != 0) 757 warn("rmdir %s", mountpoint); 758 if (ret != 0) { 759 if (unmount(dst, 0) != 0) 760 warn("unmount %s", dst); 761 errx(1, "copy %s to %s failed", mountpoint, dst); 762 } 763 764 if (mntflags & MNT_RDONLY) { 765 mntflags |= MNT_UPDATE; 766 if (mount(MOUNT_MFS, dst, mntflags, args) < 0) { 767 warn("%s: mount (update, rdonly)", dst); 768 if (unmount(dst, 0) != 0) 769 warn("unmount %s", dst); 770 exit(1); 771 } 772 } 773 } 774 775 static int 776 gettmpmnt(char *mountpoint, size_t len) 777 { 778 const char *tmp = _PATH_TMP; 779 const char *mnt = _PATH_MNT; 780 struct statfs fs; 781 size_t n; 782 783 if (statfs(tmp, &fs) != 0) 784 err(1, "statfs %s", tmp); 785 if (fs.f_flags & MNT_RDONLY) { 786 if (statfs(mnt, &fs) != 0) 787 err(1, "statfs %s", mnt); 788 if (strcmp(fs.f_mntonname, "/") != 0) 789 errx(1, "tmp mountpoint %s busy", mnt); 790 if (strlcpy(mountpoint, mnt, len) >= len) 791 errx(1, "tmp mountpoint %s too long", mnt); 792 return (0); 793 } 794 n = strlcpy(mountpoint, tmp, len); 795 if (n >= len) 796 errx(1, "tmp mount point too long"); 797 if (mountpoint[n - 1] != '/') 798 strlcat(mountpoint, "/", len); 799 n = strlcat(mountpoint, "mntXXXXXXXXXX", len); 800 if (n >= len) 801 errx(1, "tmp mount point too long"); 802 if (mkdtemp(mountpoint) == NULL) 803 err(1, "mkdtemp %s", mountpoint); 804 return (1); 805 } 806 807 #endif /* MFS */ 808