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