Merge branch 'vendor/GCC47'
[dragonfly.git] / sbin / mount_ufs / mount.c
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 __unused 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 }