1 /* 2 * Copyright (c) 1987, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifndef lint 35 static const char copyright[] = 36 "@(#) Copyright (c) 1987, 1993\n\ 37 The Regents of the University of California. All rights reserved.\n"; 38 #endif /* not lint */ 39 40 #ifndef lint 41 #if 0 42 static char sccsid[] = "From: @(#)xinstall.c 8.1 (Berkeley) 7/21/93"; 43 #endif 44 static const char rcsid[] = 45 "$FreeBSD$"; 46 #endif /* not lint */ 47 48 /*- 49 * Todo: 50 * o for -C, compare original files except in -s case. 51 * o for -C, don't change anything if nothing needs be changed. In 52 * particular, don't toggle the immutable flags just to allow null 53 * attribute changes and don't clear the dump flag. (I think inode 54 * ctimes are not updated for null attribute changes, but this is a 55 * bug.) 56 * o independent of -C, if a copy must be made, then copy to a tmpfile, 57 * set all attributes except the immutable flags, then rename, then 58 * set the immutable flags. It's annoying that the immutable flags 59 * defeat the atomicicity of rename - it seems that there must be 60 * a window where the target is not immutable. 61 */ 62 63 #include <sys/param.h> 64 #include <sys/wait.h> 65 #include <sys/mman.h> 66 #include <sys/stat.h> 67 #include <sys/mount.h> 68 69 #include <ctype.h> 70 #include <err.h> 71 #include <errno.h> 72 #include <fcntl.h> 73 #include <grp.h> 74 #include <paths.h> 75 #include <pwd.h> 76 #include <stdio.h> 77 #include <stdlib.h> 78 #include <string.h> 79 #include <unistd.h> 80 #include <sysexits.h> 81 #include <utime.h> 82 83 #include "pathnames.h" 84 85 /* Bootstrap aid - this doesn't exist in most older releases */ 86 #ifndef MAP_FAILED 87 #define MAP_FAILED ((void *)-1) /* from <sys/mman.h> */ 88 #endif 89 90 int debug, docompare, docopy, dodir, dopreserve, dostrip, nommap, verbose; 91 int mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; 92 char *group, *owner, pathbuf[MAXPATHLEN]; 93 char pathbuf2[MAXPATHLEN]; 94 95 #define DIRECTORY 0x01 /* Tell install it's a directory. */ 96 #define SETFLAGS 0x02 /* Tell install to set flags. */ 97 #define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND) 98 99 void copy __P((int, char *, int, char *, off_t)); 100 int compare __P((int, const char *, int, const char *, 101 const struct stat *, const struct stat *)); 102 void install __P((char *, char *, u_long, u_int)); 103 void install_dir __P((char *)); 104 void strip __P((char *)); 105 void usage __P((void)); 106 int trymmap __P((int)); 107 108 #define ALLOW_NUMERIC_IDS 1 109 #ifdef ALLOW_NUMERIC_IDS 110 111 uid_t uid = -1; 112 gid_t gid = -1; 113 114 uid_t resolve_uid __P((char *)); 115 gid_t resolve_gid __P((char *)); 116 u_long numeric_id __P((char *, char *)); 117 118 #else 119 120 struct passwd *pp; 121 struct group *gp; 122 123 #endif /* ALLOW_NUMERIC_IDS */ 124 125 int 126 main(argc, argv) 127 int argc; 128 char *argv[]; 129 { 130 struct stat from_sb, to_sb; 131 mode_t *set; 132 u_long fset; 133 u_int iflags; 134 int ch, no_target; 135 char *flags, *to_name; 136 137 iflags = 0; 138 while ((ch = getopt(argc, argv, "CcdDf:g:m:Mo:psv")) != -1) 139 switch((char)ch) { 140 case 'C': 141 docompare = docopy = 1; 142 break; 143 case 'c': 144 docopy = 1; 145 break; 146 case 'D': 147 debug++; 148 break; 149 case 'd': 150 dodir = 1; 151 break; 152 case 'f': 153 flags = optarg; 154 if (strtofflags(&flags, &fset, NULL)) 155 errx(EX_USAGE, "%s: invalid flag", flags); 156 iflags |= SETFLAGS; 157 break; 158 case 'g': 159 group = optarg; 160 break; 161 case 'm': 162 if (!(set = setmode(optarg))) 163 errx(EX_USAGE, "invalid file mode: %s", 164 optarg); 165 mode = getmode(set, 0); 166 free(set); 167 break; 168 case 'M': 169 nommap = 1; 170 break; 171 case 'o': 172 owner = optarg; 173 break; 174 case 'p': 175 docompare = docopy = dopreserve = 1; 176 break; 177 case 's': 178 dostrip = 1; 179 break; 180 case 'v': 181 verbose = 1; 182 break; 183 case '?': 184 default: 185 usage(); 186 } 187 argc -= optind; 188 argv += optind; 189 190 /* some options make no sense when creating directories */ 191 if (dostrip && dodir) 192 usage(); 193 194 /* must have at least two arguments, except when creating directories */ 195 if (argc < 2 && !dodir) 196 usage(); 197 198 #ifdef ALLOW_NUMERIC_IDS 199 200 if (owner) 201 uid = resolve_uid(owner); 202 if (group) 203 gid = resolve_gid(group); 204 205 #else 206 207 /* get group and owner id's */ 208 if (owner && !(pp = getpwnam(owner))) 209 errx(EX_NOUSER, "unknown user %s", owner); 210 if (group && !(gp = getgrnam(group))) 211 errx(EX_NOUSER, "unknown group %s", group); 212 213 #endif /* ALLOW_NUMERIC_IDS */ 214 215 if (dodir) { 216 for (; *argv != NULL; ++argv) 217 install_dir(*argv); 218 exit(EX_OK); 219 /* NOTREACHED */ 220 } 221 222 no_target = stat(to_name = argv[argc - 1], &to_sb); 223 if (!no_target && (to_sb.st_mode & S_IFMT) == S_IFDIR) { 224 for (; *argv != to_name; ++argv) 225 install(*argv, to_name, fset, iflags | DIRECTORY); 226 exit(EX_OK); 227 /* NOTREACHED */ 228 } 229 230 /* can't do file1 file2 directory/file */ 231 if (argc != 2) 232 usage(); 233 234 if (!no_target) { 235 if (stat(*argv, &from_sb)) 236 err(EX_OSERR, "%s", *argv); 237 if (!S_ISREG(to_sb.st_mode)) { 238 errno = EFTYPE; 239 err(EX_OSERR, "%s", to_name); 240 } 241 if (to_sb.st_dev == from_sb.st_dev && 242 to_sb.st_ino == from_sb.st_ino) 243 errx(EX_USAGE, 244 "%s and %s are the same file", *argv, to_name); 245 /* 246 * XXX - It's not at all clear why this code was here, since it completely 247 * duplicates code install(). The version in install() handles the -C flag 248 * correctly, so we'll just disable this for now. 249 */ 250 #if 0 251 /* 252 * Unlink now... avoid ETXTBSY errors later. Try and turn 253 * off the append/immutable bits -- if we fail, go ahead, 254 * it might work. 255 */ 256 if (to_sb.st_flags & NOCHANGEBITS) 257 (void)chflags(to_name, 258 to_sb.st_flags & ~(NOCHANGEBITS)); 259 (void)unlink(to_name); 260 #endif 261 } 262 install(*argv, to_name, fset, iflags); 263 exit(EX_OK); 264 /* NOTREACHED */ 265 } 266 267 #ifdef ALLOW_NUMERIC_IDS 268 269 uid_t 270 resolve_uid(s) 271 char *s; 272 { 273 struct passwd *pw; 274 275 return ((pw = getpwnam(s)) == NULL) ? 276 (uid_t) numeric_id(s, "user") : pw->pw_uid; 277 } 278 279 gid_t 280 resolve_gid(s) 281 char *s; 282 { 283 struct group *gr; 284 285 return ((gr = getgrnam(s)) == NULL) ? 286 (gid_t) numeric_id(s, "group") : gr->gr_gid; 287 } 288 289 u_long 290 numeric_id(name, type) 291 char *name, *type; 292 { 293 u_long val; 294 char *ep; 295 296 /* 297 * XXX 298 * We know that uid_t's and gid_t's are unsigned longs. 299 */ 300 errno = 0; 301 val = strtoul(name, &ep, 10); 302 if (errno) 303 err(EX_NOUSER, "%s", name); 304 if (*ep != '\0') 305 errx(EX_NOUSER, "unknown %s %s", type, name); 306 return (val); 307 } 308 309 #endif /* ALLOW_NUMERIC_IDS */ 310 311 /* 312 * install -- 313 * build a path name and install the file 314 */ 315 void 316 install(from_name, to_name, fset, flags) 317 char *from_name, *to_name; 318 u_long fset; 319 u_int flags; 320 { 321 struct stat from_sb, to_sb; 322 int devnull, from_fd, to_fd, serrno; 323 char *p, *old_to_name = 0; 324 325 if (debug >= 2 && !docompare) 326 fprintf(stderr, "install: invoked without -C for %s to %s\n", 327 from_name, to_name); 328 329 /* If try to install NULL file to a directory, fails. */ 330 if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) { 331 if (stat(from_name, &from_sb)) 332 err(EX_OSERR, "%s", from_name); 333 if (!S_ISREG(from_sb.st_mode)) { 334 errno = EFTYPE; 335 err(EX_OSERR, "%s", from_name); 336 } 337 /* Build the target path. */ 338 if (flags & DIRECTORY) { 339 (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s", 340 to_name, 341 (p = strrchr(from_name, '/')) ? ++p : from_name); 342 to_name = pathbuf; 343 } 344 devnull = 0; 345 } else { 346 from_sb.st_flags = 0; /* XXX */ 347 devnull = 1; 348 } 349 350 if (docompare) { 351 old_to_name = to_name; 352 /* 353 * Make a new temporary file in the same file system 354 * (actually, in in the same directory) as the target so 355 * that the temporary file can be renamed to the target. 356 */ 357 snprintf(pathbuf2, sizeof pathbuf2, "%s", to_name); 358 p = strrchr(pathbuf2, '/'); 359 p = (p == NULL ? pathbuf2 : p + 1); 360 snprintf(p, &pathbuf2[sizeof pathbuf2] - p, "INS@XXXX"); 361 to_fd = mkstemp(pathbuf2); 362 if (to_fd < 0) 363 /* XXX should fall back to not comparing. */ 364 err(EX_OSERR, "mkstemp: %s for %s", pathbuf2, to_name); 365 to_name = pathbuf2; 366 } else { 367 /* 368 * Unlink now... avoid errors later. Try to turn off the 369 * append/immutable bits -- if we fail, go ahead, it might 370 * work. 371 */ 372 if (stat(to_name, &to_sb) == 0 && to_sb.st_flags & NOCHANGEBITS) 373 (void)chflags(to_name, to_sb.st_flags & ~NOCHANGEBITS); 374 unlink(to_name); 375 376 /* Create target. */ 377 to_fd = open(to_name, 378 O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR); 379 if (to_fd < 0) 380 err(EX_OSERR, "%s", to_name); 381 } 382 383 if (!devnull) { 384 if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) { 385 serrno = errno; 386 (void)unlink(to_name); 387 errno = serrno; 388 err(EX_OSERR, "%s", from_name); 389 } 390 copy(from_fd, from_name, to_fd, to_name, from_sb.st_size); 391 (void)close(from_fd); 392 } 393 394 if (dostrip) { 395 (void)close(to_fd); 396 397 strip(to_name); 398 399 /* Reopen target. */ 400 to_fd = open(to_name, O_RDWR, 0); 401 if (to_fd < 0) 402 err(EX_OSERR, "%s", to_name); 403 } 404 405 /* 406 * Unfortunately, because we strip the installed file and not the 407 * original one, it is impossible to do the comparison without 408 * first laboriously copying things over and then comparing. 409 * It may be possible to better optimize the !dostrip case, however. 410 * For further study. 411 */ 412 if (docompare) { 413 struct stat old_sb, new_sb, timestamp_sb; 414 int old_fd; 415 struct utimbuf utb; 416 417 old_fd = open(old_to_name, O_RDONLY, 0); 418 if (old_fd < 0 && errno == ENOENT) 419 goto different; 420 if (old_fd < 0) 421 err(EX_OSERR, "%s", old_to_name); 422 fstat(old_fd, &old_sb); 423 if (old_sb.st_flags & NOCHANGEBITS) 424 (void)fchflags(old_fd, old_sb.st_flags & ~NOCHANGEBITS); 425 fstat(to_fd, &new_sb); 426 if (compare(old_fd, old_to_name, to_fd, to_name, &old_sb, 427 &new_sb)) { 428 different: 429 if (debug != 0) 430 fprintf(stderr, 431 "install: renaming for %s: %s to %s\n", 432 from_name, to_name, old_to_name); 433 if (verbose != 0) 434 printf("install: %s -> %s\n", 435 from_name, old_to_name); 436 if (dopreserve && stat(from_name, ×tamp_sb) == 0) { 437 utb.actime = timestamp_sb.st_atime; 438 utb.modtime = timestamp_sb.st_mtime; 439 (void)utime(to_name, &utb); 440 } 441 moveit: 442 if (rename(to_name, old_to_name) < 0) { 443 serrno = errno; 444 unlink(to_name); 445 unlink(old_to_name); 446 errno = serrno; 447 err(EX_OSERR, "rename: %s to %s", to_name, 448 old_to_name); 449 } 450 close(old_fd); 451 } else { 452 if (old_sb.st_nlink != 1) { 453 /* 454 * Replace the target, although it hasn't 455 * changed, to snap the extra links. But 456 * preserve the target file times. 457 */ 458 if (fstat(old_fd, ×tamp_sb) == 0) { 459 utb.actime = timestamp_sb.st_atime; 460 utb.modtime = timestamp_sb.st_mtime; 461 (void)utime(to_name, &utb); 462 } 463 goto moveit; 464 } 465 if (unlink(to_name) < 0) 466 err(EX_OSERR, "unlink: %s", to_name); 467 close(to_fd); 468 to_fd = old_fd; 469 } 470 to_name = old_to_name; 471 } 472 473 /* 474 * Set owner, group, mode for target; do the chown first, 475 * chown may lose the setuid bits. 476 */ 477 if ((group || owner) && 478 #ifdef ALLOW_NUMERIC_IDS 479 fchown(to_fd, owner ? uid : -1, group ? gid : -1)) { 480 #else 481 fchown(to_fd, owner ? pp->pw_uid : -1, group ? gp->gr_gid : -1)) { 482 #endif 483 serrno = errno; 484 (void)unlink(to_name); 485 errno = serrno; 486 err(EX_OSERR,"%s: chown/chgrp", to_name); 487 } 488 if (fchmod(to_fd, mode)) { 489 serrno = errno; 490 (void)unlink(to_name); 491 errno = serrno; 492 err(EX_OSERR, "%s: chmod", to_name); 493 } 494 495 /* 496 * If provided a set of flags, set them, otherwise, preserve the 497 * flags, except for the dump flag. 498 * NFS does not support flags. Ignore EOPNOTSUPP flags if we're just 499 * trying to turn off UF_NODUMP. If we're trying to set real flags, 500 * then warn if the the fs doesn't support it, otherwise fail. 501 */ 502 if (fchflags(to_fd, 503 flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) { 504 if (flags & SETFLAGS) { 505 if (errno == EOPNOTSUPP) 506 warn("%s: chflags", to_name); 507 else { 508 serrno = errno; 509 (void)unlink(to_name); 510 errno = serrno; 511 err(EX_OSERR, "%s: chflags", to_name); 512 } 513 } 514 } 515 516 (void)close(to_fd); 517 if (!docopy && !devnull && unlink(from_name)) 518 err(EX_OSERR, "%s", from_name); 519 } 520 521 /* 522 * compare -- 523 * compare two files; non-zero means files differ 524 */ 525 int 526 compare(int from_fd, const char *from_name, int to_fd, const char *to_name, 527 const struct stat *from_sb, const struct stat *to_sb) 528 { 529 char *p, *q; 530 int rv; 531 size_t tsize; 532 int done_compare; 533 534 rv = 0; 535 if (from_sb->st_size != to_sb->st_size) 536 return 1; 537 538 tsize = (size_t)from_sb->st_size; 539 540 if (tsize <= 8 * 1024 * 1024) { 541 done_compare = 0; 542 if (trymmap(from_fd) && trymmap(to_fd)) { 543 p = mmap(NULL, tsize, PROT_READ, MAP_SHARED, from_fd, (off_t)0); 544 if (p == (char *)MAP_FAILED) 545 goto out; 546 q = mmap(NULL, tsize, PROT_READ, MAP_SHARED, to_fd, (off_t)0); 547 if (q == (char *)MAP_FAILED) { 548 munmap(p, tsize); 549 goto out; 550 } 551 552 rv = memcmp(p, q, tsize); 553 munmap(p, tsize); 554 munmap(q, tsize); 555 done_compare = 1; 556 } 557 out: 558 if (!done_compare) { 559 char buf1[MAXBSIZE]; 560 char buf2[MAXBSIZE]; 561 int n1, n2; 562 563 rv = 0; 564 lseek(from_fd, 0, SEEK_SET); 565 lseek(to_fd, 0, SEEK_SET); 566 while (rv == 0) { 567 n1 = read(from_fd, buf1, sizeof(buf1)); 568 if (n1 == 0) 569 break; /* EOF */ 570 else if (n1 > 0) { 571 n2 = read(to_fd, buf2, n1); 572 if (n2 == n1) 573 rv = memcmp(buf1, buf2, n1); 574 else 575 rv = 1; /* out of sync */ 576 } else 577 rv = 1; /* read failure */ 578 } 579 lseek(from_fd, 0, SEEK_SET); 580 lseek(to_fd, 0, SEEK_SET); 581 } 582 } else 583 rv = 1; /* don't bother in this case */ 584 585 return rv; 586 } 587 588 /* 589 * copy -- 590 * copy from one file to another 591 */ 592 void 593 copy(from_fd, from_name, to_fd, to_name, size) 594 register int from_fd, to_fd; 595 char *from_name, *to_name; 596 off_t size; 597 { 598 register int nr, nw; 599 int serrno; 600 char *p, buf[MAXBSIZE]; 601 int done_copy; 602 603 /* 604 * Mmap and write if less than 8M (the limit is so we don't totally 605 * trash memory on big files. This is really a minor hack, but it 606 * wins some CPU back. 607 */ 608 done_copy = 0; 609 if (size <= 8 * 1048576 && trymmap(from_fd)) { 610 if ((p = mmap(NULL, (size_t)size, PROT_READ, 611 MAP_SHARED, from_fd, (off_t)0)) == (char *)MAP_FAILED) 612 goto out; 613 if ((nw = write(to_fd, p, size)) != size) { 614 serrno = errno; 615 (void)unlink(to_name); 616 errno = nw > 0 ? EIO : serrno; 617 err(EX_OSERR, "%s", to_name); 618 } 619 done_copy = 1; 620 out: 621 } 622 if (!done_copy) { 623 while ((nr = read(from_fd, buf, sizeof(buf))) > 0) 624 if ((nw = write(to_fd, buf, nr)) != nr) { 625 serrno = errno; 626 (void)unlink(to_name); 627 errno = nw > 0 ? EIO : serrno; 628 err(EX_OSERR, "%s", to_name); 629 } 630 if (nr != 0) { 631 serrno = errno; 632 (void)unlink(to_name); 633 errno = serrno; 634 err(EX_OSERR, "%s", from_name); 635 } 636 } 637 } 638 639 /* 640 * strip -- 641 * use strip(1) to strip the target file 642 */ 643 void 644 strip(to_name) 645 char *to_name; 646 { 647 int serrno, status; 648 649 switch (fork()) { 650 case -1: 651 serrno = errno; 652 (void)unlink(to_name); 653 errno = serrno; 654 err(EX_TEMPFAIL, "fork"); 655 case 0: 656 execlp("strip", "strip", to_name, NULL); 657 err(EX_OSERR, "exec(strip)"); 658 default: 659 if (wait(&status) == -1 || status) { 660 (void)unlink(to_name); 661 exit(EX_SOFTWARE); 662 /* NOTREACHED */ 663 } 664 } 665 } 666 667 /* 668 * install_dir -- 669 * build directory heirarchy 670 */ 671 void 672 install_dir(path) 673 char *path; 674 { 675 register char *p; 676 struct stat sb; 677 int ch; 678 679 for (p = path;; ++p) 680 if (!*p || (p != path && *p == '/')) { 681 ch = *p; 682 *p = '\0'; 683 if (stat(path, &sb)) { 684 if (errno != ENOENT || mkdir(path, 0755) < 0) { 685 err(EX_OSERR, "mkdir %s", path); 686 /* NOTREACHED */ 687 } 688 } else if (!S_ISDIR(sb.st_mode)) 689 errx(EX_OSERR, "%s exists but is not a directory", path); 690 if (!(*p = ch)) 691 break; 692 } 693 694 if ((gid != (gid_t)-1 || uid != (uid_t)-1) && chown(path, uid, gid)) 695 warn("chown %u:%u %s", uid, gid, path); 696 if (chmod(path, mode)) 697 warn("chmod %o %s", mode, path); 698 } 699 700 /* 701 * usage -- 702 * print a usage message and die 703 */ 704 void 705 usage() 706 { 707 (void)fprintf(stderr,"\ 708 usage: install [-CcDpsv] [-f flags] [-g group] [-m mode] [-o owner] file1 file2\n\ 709 install [-CcDpsv] [-f flags] [-g group] [-m mode] [-o owner] file1 ...\n\ 710 fileN directory\n\ 711 install -d [-v] [-g group] [-m mode] [-o owner] directory ...\n"); 712 exit(EX_USAGE); 713 /* NOTREACHED */ 714 } 715 716 /* 717 * trymmap -- 718 * return true (1) if mmap should be tried, false (0) if not. 719 */ 720 int 721 trymmap(fd) 722 int fd; 723 { 724 /* 725 * The ifdef is for bootstrapping - f_fstypename doesn't exist in 726 * pre-Lite2-merge systems. 727 */ 728 #ifdef MFSNAMELEN 729 struct statfs stfs; 730 731 if (nommap || fstatfs(fd, &stfs) != 0) 732 return (0); 733 if (strcmp(stfs.f_fstypename, "ufs") == 0 || 734 strcmp(stfs.f_fstypename, "cd9660") == 0) 735 return (1); 736 #endif 737 return (0); 738 } 739