1 /*- 2 * Copyright (c) 1980, 1989, 1993, 1994 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 * @(#) Copyright (c) 1980, 1989, 1993, 1994 The Regents of the University of California. All rights reserved. 34 * @(#)mount.c 8.25 (Berkeley) 5/8/95 35 * $FreeBSD: src/sbin/mount/mount.c,v 1.39.2.3 2001/08/01 08:26:23 obrien Exp $ 36 * $DragonFly: src/sbin/mount/mount.c,v 1.10 2005/04/03 17:13:08 joerg Exp $ 37 */ 38 39 #include <sys/param.h> 40 #include <sys/mount.h> 41 #include <sys/mountctl.h> 42 #include <sys/stat.h> 43 #include <sys/wait.h> 44 45 #include <err.h> 46 #include <errno.h> 47 #include <fstab.h> 48 #include <pwd.h> 49 #include <signal.h> 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <string.h> 53 #include <unistd.h> 54 55 #include "extern.h" 56 #include "mntopts.h" 57 #include "pathnames.h" 58 59 /* `meta' options */ 60 #define MOUNT_META_OPTION_FSTAB "fstab" 61 #define MOUNT_META_OPTION_CURRENT "current" 62 63 int debug, fstab_style, verbose; 64 65 static char *catopt(char *, const char *); 66 static struct statfs 67 *getmntpt(const char *); 68 static int hasopt(const char *, const char *); 69 static int ismounted(struct fstab *, struct statfs *, int); 70 static int isremountable(const char *); 71 static void mangle(char *, int *, const char **); 72 static char *update_options(char *, char *, int); 73 static int mountfs(const char *, const char *, const char *, 74 int, const char *, const char *); 75 static void remopt(char *, const char *); 76 static void printdefvals(const struct statfs *); 77 static void prmount(struct statfs *); 78 static void putfsent(const struct statfs *); 79 static void usage(void); 80 static char *flags2opts(int); 81 static char *xstrdup(const char *str); 82 83 /* mount_ufs.c */ 84 int mount_ufs(int, const char **); 85 86 /* 87 * List of VFS types that can be remounted without becoming mounted on top 88 * of each other. 89 * XXX Is this list correct? 90 */ 91 static const char * 92 remountable_fs_names[] = { 93 "ufs", "ffs", "ext2fs", 94 0 95 }; 96 97 int 98 main(int argc, char **argv) 99 { 100 const char *mntfromname, **vfslist, *vfstype; 101 struct fstab *fs; 102 struct statfs *mntbuf; 103 FILE *mountdfp; 104 pid_t pid; 105 int all, ch, i, init_flags, mntsize, rval, have_fstab; 106 char *options; 107 108 all = init_flags = 0; 109 options = NULL; 110 vfslist = NULL; 111 vfstype = "ufs"; 112 while ((ch = getopt(argc, argv, "adF:fo:prwt:uv")) != -1) 113 switch (ch) { 114 case 'a': 115 all = 1; 116 break; 117 case 'd': 118 debug = 1; 119 break; 120 case 'F': 121 setfstab(optarg); 122 break; 123 case 'f': 124 init_flags |= MNT_FORCE; 125 break; 126 case 'o': 127 if (*optarg) 128 options = catopt(options, optarg); 129 break; 130 case 'p': 131 fstab_style = 1; 132 verbose = 1; 133 break; 134 case 'r': 135 options = catopt(options, "ro"); 136 break; 137 case 't': 138 if (vfslist != NULL) 139 errx(1, "only one -t option may be specified"); 140 vfslist = makevfslist(optarg); 141 vfstype = optarg; 142 break; 143 case 'u': 144 init_flags |= MNT_UPDATE; 145 break; 146 case 'v': 147 verbose = 1; 148 break; 149 case 'w': 150 options = catopt(options, "noro"); 151 break; 152 case '?': 153 default: 154 usage(); 155 /* NOTREACHED */ 156 } 157 argc -= optind; 158 argv += optind; 159 160 #define BADTYPE(type) \ 161 (strcmp(type, FSTAB_RO) && \ 162 strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ)) 163 164 rval = 0; 165 switch (argc) { 166 case 0: 167 if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) 168 err(1, "getmntinfo"); 169 if (all) { 170 while ((fs = getfsent()) != NULL) { 171 if (BADTYPE(fs->fs_type)) 172 continue; 173 if (checkvfsname(fs->fs_vfstype, vfslist)) 174 continue; 175 if (hasopt(fs->fs_mntops, "noauto")) 176 continue; 177 if (!(init_flags & MNT_UPDATE) && 178 ismounted(fs, mntbuf, mntsize)) 179 continue; 180 options = update_options(options, 181 fs->fs_mntops, mntbuf->f_flags); 182 if (mountfs(fs->fs_vfstype, fs->fs_spec, 183 fs->fs_file, init_flags, options, 184 fs->fs_mntops)) 185 rval = 1; 186 } 187 } else if (fstab_style) { 188 for (i = 0; i < mntsize; i++) { 189 if (checkvfsname(mntbuf[i].f_fstypename, vfslist)) 190 continue; 191 putfsent(&mntbuf[i]); 192 } 193 } else { 194 for (i = 0; i < mntsize; i++) { 195 if (checkvfsname(mntbuf[i].f_fstypename, 196 vfslist)) 197 continue; 198 prmount(&mntbuf[i]); 199 } 200 } 201 exit(rval); 202 case 1: 203 if (vfslist != NULL) 204 usage(); 205 206 rmslashes(*argv, *argv); 207 208 if (init_flags & MNT_UPDATE) { 209 mntfromname = NULL; 210 have_fstab = 0; 211 if ((mntbuf = getmntpt(*argv)) == NULL) 212 errx(1, "not currently mounted %s", *argv); 213 /* 214 * Only get the mntflags from fstab if both mntpoint 215 * and mntspec are identical. Also handle the special 216 * case where just '/' is mounted and 'spec' is not 217 * identical with the one from fstab ('/dev' is missing 218 * in the spec-string at boot-time). 219 */ 220 if ((fs = getfsfile(mntbuf->f_mntonname)) != NULL) { 221 if (strcmp(fs->fs_spec, 222 mntbuf->f_mntfromname) == 0 && 223 strcmp(fs->fs_file, 224 mntbuf->f_mntonname) == 0) { 225 have_fstab = 1; 226 mntfromname = mntbuf->f_mntfromname; 227 } else if (argv[0][0] == '/' && 228 argv[0][1] == '\0') { 229 fs = getfsfile("/"); 230 have_fstab = 1; 231 mntfromname = fs->fs_spec; 232 } 233 } 234 if (have_fstab) { 235 options = update_options(options, fs->fs_mntops, 236 mntbuf->f_flags); 237 } else { 238 mntfromname = mntbuf->f_mntfromname; 239 options = update_options(options, NULL, 240 mntbuf->f_flags); 241 } 242 rval = mountfs(mntbuf->f_fstypename, mntfromname, 243 mntbuf->f_mntonname, init_flags, options, 0); 244 break; 245 } 246 if ((fs = getfsfile(*argv)) == NULL && 247 (fs = getfsspec(*argv)) == NULL) 248 errx(1, "%s: unknown special file or file system", 249 *argv); 250 if (BADTYPE(fs->fs_type)) 251 errx(1, "%s has unknown file system type", 252 *argv); 253 rval = mountfs(fs->fs_vfstype, fs->fs_spec, fs->fs_file, 254 init_flags, options, fs->fs_mntops); 255 break; 256 case 2: 257 /* 258 * If -t flag has not been specified, the path cannot be 259 * found, spec contains either a ':' or a '@', and the 260 * spec is not a file with those characters, then assume 261 * that an NFS filesystem is being specified ala Sun. 262 */ 263 if (vfslist == NULL && strpbrk(argv[0], ":@") != NULL && 264 access(argv[0], 0) == -1) 265 vfstype = "nfs"; 266 rval = mountfs(vfstype, getdevpath(argv[0], 0), argv[1], 267 init_flags, options, NULL); 268 break; 269 default: 270 usage(); 271 /* NOTREACHED */ 272 } 273 274 /* 275 * If the mount was successfully, and done by root, tell mountd the 276 * good news. Pid checks are probably unnecessary, but don't hurt. 277 */ 278 if (rval == 0 && getuid() == 0 && 279 (mountdfp = fopen(_PATH_MOUNTDPID, "r")) != NULL) { 280 if (fscanf(mountdfp, "%d", &pid) == 1 && 281 pid > 0 && kill(pid, SIGHUP) == -1 && errno != ESRCH) 282 err(1, "signal mountd"); 283 fclose(mountdfp); 284 } 285 286 exit(rval); 287 } 288 289 static int 290 ismounted(struct fstab *fs, struct statfs *mntbuf, int mntsize) 291 { 292 int i; 293 294 if (fs->fs_file[0] == '/' && fs->fs_file[1] == '\0') 295 /* the root file system can always be remounted */ 296 return (0); 297 298 for (i = mntsize - 1; i >= 0; --i) 299 if (strcmp(fs->fs_file, mntbuf[i].f_mntonname) == 0 && 300 (!isremountable(fs->fs_vfstype) || 301 strcmp(fs->fs_spec, mntbuf[i].f_mntfromname) == 0)) 302 return (1); 303 return (0); 304 } 305 306 static int 307 isremountable(const char *vfsname) 308 { 309 const char **cp; 310 311 for (cp = remountable_fs_names; *cp; cp++) 312 if (strcmp(*cp, vfsname) == 0) 313 return (1); 314 return (0); 315 } 316 317 static int 318 hasopt(const char *mntopts, const char *option) 319 { 320 int negative, found; 321 char *opt, *optbuf; 322 323 if (option[0] == 'n' && option[1] == 'o') { 324 negative = 1; 325 option += 2; 326 } else 327 negative = 0; 328 optbuf = xstrdup(mntopts); 329 found = 0; 330 for (opt = optbuf; (opt = strtok(opt, ",")) != NULL; opt = NULL) { 331 if (opt[0] == 'n' && opt[1] == 'o') { 332 if (!strcasecmp(opt + 2, option)) 333 found = negative; 334 } else if (!strcasecmp(opt, option)) 335 found = !negative; 336 } 337 free(optbuf); 338 return (found); 339 } 340 341 static int 342 mountfs(const char *vfstype, const char *spec, const char *name, int flags, 343 const char *options, const char *mntopts) 344 { 345 /* List of directories containing mount_xxx subcommands. */ 346 static const char *edirs[] = { 347 _PATH_SBIN, 348 _PATH_USRSBIN, 349 NULL 350 }; 351 const char *argv[100], **edir; 352 struct statfs sf; 353 pid_t pid; 354 int argc, i, status; 355 char *optbuf, execname[MAXPATHLEN + 1], mntpath[MAXPATHLEN]; 356 357 #if __GNUC__ 358 (void)&optbuf; 359 (void)&name; 360 #endif 361 362 /* resolve the mountpoint with realpath(3) */ 363 checkpath(name, mntpath); 364 name = mntpath; 365 366 if (mntopts == NULL) 367 mntopts = ""; 368 if (options == NULL) { 369 if (*mntopts == '\0') { 370 options = "rw"; 371 } else { 372 options = mntopts; 373 mntopts = ""; 374 } 375 } 376 optbuf = catopt(xstrdup(mntopts), options); 377 378 if (strcmp(name, "/") == 0) 379 flags |= MNT_UPDATE; 380 if (flags & MNT_FORCE) 381 optbuf = catopt(optbuf, "force"); 382 if (flags & MNT_RDONLY) 383 optbuf = catopt(optbuf, "ro"); 384 /* 385 * XXX 386 * The mount_mfs (newfs) command uses -o to select the 387 * optimization mode. We don't pass the default "-o rw" 388 * for that reason. 389 */ 390 if (flags & MNT_UPDATE) 391 optbuf = catopt(optbuf, "update"); 392 393 argc = 0; 394 argv[argc++] = vfstype; 395 mangle(optbuf, &argc, argv); 396 argv[argc++] = spec; 397 argv[argc++] = name; 398 argv[argc] = NULL; 399 400 if (debug) { 401 printf("exec: mount_%s", vfstype); 402 for (i = 1; i < argc; i++) 403 printf(" %s", argv[i]); 404 printf("\n"); 405 return (0); 406 } 407 408 switch (pid = fork()) { 409 case -1: /* Error. */ 410 warn("fork"); 411 free(optbuf); 412 return (1); 413 case 0: /* Child. */ 414 if (strcmp(vfstype, "ufs") == 0) 415 exit(mount_ufs(argc, argv)); 416 417 /* Go find an executable. */ 418 for (edir = edirs; *edir; edir++) { 419 snprintf(execname, 420 sizeof(execname), "%s/mount_%s", *edir, vfstype); 421 execv(execname, __DECONST(char * const *, argv)); 422 } 423 if (errno == ENOENT) { 424 int len = 0; 425 char *cp; 426 for (edir = edirs; *edir; edir++) 427 len += strlen(*edir) + 2; /* ", " */ 428 if ((cp = malloc(len)) == NULL) 429 errx(1, "malloc failed"); 430 cp[0] = '\0'; 431 for (edir = edirs; *edir; edir++) { 432 strcat(cp, *edir); 433 if (edir[1] != NULL) 434 strcat(cp, ", "); 435 } 436 warn("exec mount_%s not found in %s", vfstype, cp); 437 } 438 exit(1); 439 /* NOTREACHED */ 440 default: /* Parent. */ 441 free(optbuf); 442 443 if (waitpid(pid, &status, 0) < 0) { 444 warn("waitpid"); 445 return (1); 446 } 447 448 if (WIFEXITED(status)) { 449 if (WEXITSTATUS(status) != 0) 450 return (WEXITSTATUS(status)); 451 } else if (WIFSIGNALED(status)) { 452 warnx("%s: %s", name, sys_siglist[WTERMSIG(status)]); 453 return (1); 454 } 455 456 if (verbose) { 457 if (statfs(name, &sf) < 0) { 458 warn("statfs %s", name); 459 return (1); 460 } 461 if (fstab_style) 462 putfsent(&sf); 463 else 464 prmount(&sf); 465 } 466 break; 467 } 468 469 return (0); 470 } 471 472 static void 473 prmount(struct statfs *sfp) 474 { 475 struct passwd *pw; 476 char *buf; 477 int error; 478 int len; 479 480 error = 0; 481 len = 256; 482 483 if ((buf = malloc(len)) == NULL) 484 errx(1, "malloc failed"); 485 486 printf("%s on %s (%s", sfp->f_mntfromname, sfp->f_mntonname, 487 sfp->f_fstypename); 488 489 /* Get a string buffer with all the used flags names */ 490 error = mountctl(sfp->f_mntonname, MOUNTCTL_MOUNTFLAGS, 491 -1, NULL, 0, buf, len); 492 493 if (sfp->f_owner) { 494 printf(", mounted by "); 495 if ((pw = getpwuid(sfp->f_owner)) != NULL) 496 printf("%s", pw->pw_name); 497 else 498 printf("%d", sfp->f_owner); 499 } 500 501 if (strlen(buf)) 502 printf(", %s", buf); 503 504 if (verbose) { 505 if (sfp->f_syncwrites != 0 || sfp->f_asyncwrites != 0) 506 printf(", writes: sync %ld async %ld", 507 sfp->f_syncwrites, sfp->f_asyncwrites); 508 if (sfp->f_syncreads != 0 || sfp->f_asyncreads != 0) 509 printf(", reads: sync %ld async %ld", 510 sfp->f_syncreads, sfp->f_asyncreads); 511 } 512 printf(")\n"); 513 } 514 515 static struct statfs * 516 getmntpt(const char *name) 517 { 518 struct statfs *mntbuf; 519 int i, mntsize; 520 521 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); 522 for (i = mntsize - 1; i >= 0; i--) { 523 if (strcmp(mntbuf[i].f_mntfromname, name) == 0 || 524 strcmp(mntbuf[i].f_mntonname, name) == 0) 525 return (&mntbuf[i]); 526 } 527 return (NULL); 528 } 529 530 static char * 531 catopt(char *s0, const char *s1) 532 { 533 size_t i; 534 char *cp; 535 536 if (s1 == NULL || *s1 == '\0') 537 return s0; 538 539 if (s0 && *s0) { 540 i = strlen(s0) + strlen(s1) + 1 + 1; 541 if ((cp = malloc(i)) == NULL) 542 errx(1, "malloc failed"); 543 snprintf(cp, i, "%s,%s", s0, s1); 544 } else 545 cp = xstrdup(s1); 546 547 if (s0) 548 free(s0); 549 return (cp); 550 } 551 552 static void 553 mangle(char *options, int *argcp, const char **argv) 554 { 555 char *p, *s; 556 int argc; 557 558 argc = *argcp; 559 for (s = options; (p = strsep(&s, ",")) != NULL;) 560 if (*p != '\0') { 561 if (*p == '-') { 562 argv[argc++] = p; 563 p = strchr(p, '='); 564 if (p) { 565 *p = '\0'; 566 argv[argc++] = p+1; 567 } 568 } else if (strcmp(p, "rw") != 0) { 569 argv[argc++] = "-o"; 570 argv[argc++] = p; 571 } 572 } 573 574 *argcp = argc; 575 } 576 577 578 static char * 579 update_options(char *opts, char *fstab, int curflags) 580 { 581 char *o, *p; 582 char *cur; 583 char *expopt, *newopt, *tmpopt; 584 585 if (opts == NULL) 586 return xstrdup(""); 587 588 /* remove meta options from list */ 589 remopt(fstab, MOUNT_META_OPTION_FSTAB); 590 remopt(fstab, MOUNT_META_OPTION_CURRENT); 591 cur = flags2opts(curflags); 592 593 /* 594 * Expand all meta-options passed to us first. 595 */ 596 expopt = NULL; 597 for (p = opts; (o = strsep(&p, ",")) != NULL;) { 598 if (strcmp(MOUNT_META_OPTION_FSTAB, o) == 0) 599 expopt = catopt(expopt, fstab); 600 else if (strcmp(MOUNT_META_OPTION_CURRENT, o) == 0) 601 expopt = catopt(expopt, cur); 602 else 603 expopt = catopt(expopt, o); 604 } 605 free(cur); 606 free(opts); 607 608 /* 609 * Remove previous contradictory arguments. Given option "foo" we 610 * remove all the "nofoo" options. Given "nofoo" we remove "nonofoo" 611 * and "foo" - so we can deal with possible options like "notice". 612 */ 613 newopt = NULL; 614 for (p = expopt; (o = strsep(&p, ",")) != NULL;) { 615 if ((tmpopt = malloc( strlen(o) + 2 + 1 )) == NULL) 616 errx(1, "malloc failed"); 617 618 strcpy(tmpopt, "no"); 619 strcat(tmpopt, o); 620 remopt(newopt, tmpopt); 621 free(tmpopt); 622 623 if (strncmp("no", o, 2) == 0) 624 remopt(newopt, o+2); 625 626 newopt = catopt(newopt, o); 627 } 628 free(expopt); 629 630 return newopt; 631 } 632 633 static void 634 remopt(char *string, const char *opt) 635 { 636 char *o, *p, *r; 637 638 if (string == NULL || *string == '\0' || opt == NULL || *opt == '\0') 639 return; 640 641 r = string; 642 643 for (p = string; (o = strsep(&p, ",")) != NULL;) { 644 if (strcmp(opt, o) != 0) { 645 if (*r == ',' && *o != '\0') 646 r++; 647 while ((*r++ = *o++) != '\0') 648 ; 649 *--r = ','; 650 } 651 } 652 *r = '\0'; 653 } 654 655 static void 656 usage(void) 657 { 658 659 fprintf(stderr, "%s\n%s\n%s\n", 660 "usage: mount [-adfpruvw] [-F fstab] [-o options] [-t type]", 661 " mount [-dfpruvw] {special | node}", 662 " mount [-dfpruvw] [-o options] [-t type] special node"); 663 exit(1); 664 } 665 666 /* 667 * Prints the default values for dump frequency and pass number of fsck. 668 * This happens when mount(8) is called with -p and there is no fstab file 669 * or there is but the values aren't specified. 670 */ 671 static void 672 printdefvals(const struct statfs *ent) 673 { 674 if (strcmp(ent->f_fstypename, "ufs") == 0) { 675 if (strcmp(ent->f_mntonname, "/") == 0) 676 printf("\t1 1\n"); 677 else 678 printf("\t2 2\n"); 679 } else { 680 printf("\t0 0\n"); 681 } 682 } 683 684 static void 685 putfsent(const struct statfs *ent) 686 { 687 struct stat sb; 688 struct fstab *fst; 689 char *opts; 690 691 opts = flags2opts(ent->f_flags); 692 printf("%s\t%s\t%s %s", ent->f_mntfromname, ent->f_mntonname, 693 ent->f_fstypename, opts); 694 free(opts); 695 696 /* 697 * If fstab file is missing don't call getfsspec() or getfsfile() 698 * at all, because the same warning will be printed twice for every 699 * mounted filesystem. 700 */ 701 if (stat(_PATH_FSTAB, &sb) != -1) { 702 if ((fst = getfsspec(ent->f_mntfromname))) 703 printf("\t%u %u\n", fst->fs_freq, fst->fs_passno); 704 else if ((fst = getfsfile(ent->f_mntonname))) 705 printf("\t%u %u\n", fst->fs_freq, fst->fs_passno); 706 else 707 printdefvals(ent); 708 } else { 709 printdefvals(ent); 710 } 711 } 712 713 714 static char * 715 flags2opts(int flags) 716 { 717 char *res; 718 719 res = NULL; 720 721 res = catopt(res, (flags & MNT_RDONLY) ? "ro" : "rw"); 722 723 if (flags & MNT_SYNCHRONOUS) res = catopt(res, "sync"); 724 if (flags & MNT_NOEXEC) res = catopt(res, "noexec"); 725 if (flags & MNT_NOSUID) res = catopt(res, "nosuid"); 726 if (flags & MNT_NODEV) res = catopt(res, "nodev"); 727 if (flags & MNT_UNION) res = catopt(res, "union"); 728 if (flags & MNT_ASYNC) res = catopt(res, "async"); 729 if (flags & MNT_NOATIME) res = catopt(res, "noatime"); 730 if (flags & MNT_NOCLUSTERR) res = catopt(res, "noclusterr"); 731 if (flags & MNT_NOCLUSTERW) res = catopt(res, "noclusterw"); 732 if (flags & MNT_NOSYMFOLLOW) res = catopt(res, "nosymfollow"); 733 if (flags & MNT_SUIDDIR) res = catopt(res, "suiddir"); 734 if (flags & MNT_IGNORE) res = catopt(res, "ignore"); 735 736 return res; 737 } 738 739 static char* 740 xstrdup(const char *str) 741 { 742 char* ret = strdup(str); 743 if(ret == NULL) { 744 errx(1, "strdup failed (could not allocate memory)"); 745 } 746 return ret; 747 } 748