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