/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * mount */ #include #include #include #include #include #include #include #include #include #include #define bcopy(f, t, n) memcpy(t, f, n) #define bzero(s, n) memset(s, 0, n) #define bcmp(s, d, n) memcmp(s, d, n) #define index(s, r) strchr(s, r) #define rindex(s, r) strrchr(s, r) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int ro = 0; static int largefiles = 0; /* flag - add default nolargefiles to mnttab */ static int gflg = 0; static int mflg = 0; static int Oflg = 0; static int qflg = 0; #define NAME_MAX 64 /* sizeof "fstype myname" */ static int checkislog(char *); static void disable_logging(char *, char *); static int eatmntopt(struct mnttab *, char *); static void enable_logging(char *, char *); static void fixopts(struct mnttab *, char *); static void mountfs(struct mnttab *); static void replace_opts(char *, int, char *, char *); static int replace_opts_dflt(char *, int, const char *, const char *); static void rmopt(struct mnttab *, char *); static void rpterr(char *, char *); static void usage(void); static char fstype[] = MNTTYPE_UFS; static char opts[MAX_MNTOPT_STR]; static char typename[NAME_MAX], *myname; static char *fop_subopts[] = { MNTOPT_ONERROR, NULL }; #define NOMATCH (-1) #define ONERROR (0) /* index within fop_subopts */ static struct fop_subopt { char *str; int flag; } fop_subopt_list[] = { { UFSMNT_ONERROR_PANIC_STR, UFSMNT_ONERROR_PANIC }, { UFSMNT_ONERROR_LOCK_STR, UFSMNT_ONERROR_LOCK }, { UFSMNT_ONERROR_UMOUNT_STR, UFSMNT_ONERROR_UMOUNT }, { NULL, UFSMNT_ONERROR_DEFAULT } }; /* * Check if the specified filesystem is already mounted. */ static boolean_t in_mnttab(char *mountp) { FILE *file; int found = B_FALSE; struct mnttab mntent; if ((file = fopen(MNTTAB, "r")) == NULL) return (B_FALSE); while (getmntent(file, &mntent) == 0) { if (mntent.mnt_mountp != NULL && strcmp(mntent.mnt_mountp, mountp) == 0 && mntent.mnt_fstype != NULL && strcmp(mntent.mnt_fstype, MNTTYPE_UFS) == 0) { found = B_TRUE; break; } } (void) fclose(file); return (found); } /* * Find opt in mntopt */ static char * findopt(char *mntopt, char *opt) { int nc, optlen = strlen(opt); while (*mntopt) { nc = strcspn(mntopt, ", ="); if (strncmp(mntopt, opt, nc) == 0) if (optlen == nc) return (mntopt); mntopt += nc; mntopt += strspn(mntopt, ", ="); } return (NULL); } int main(int argc, char *argv[]) { struct mnttab mnt; int c; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif (void) textdomain(TEXT_DOMAIN); myname = strrchr(argv[0], '/'); if (myname) myname++; else myname = argv[0]; (void) snprintf(typename, sizeof (typename), "%s %s", fstype, myname); argv[0] = typename; opts[0] = '\0'; /* * Set options */ while ((c = getopt(argc, argv, "gmo:pqrVO")) != EOF) { switch (c) { case 'g': gflg++; break; case 'o': if (strlcpy(opts, optarg, sizeof (opts)) >= sizeof (opts)) { (void) fprintf(stderr, gettext("option string " "argument too long\n")); } break; case 'O': Oflg++; break; case 'r': ro++; break; case 'm': mflg++; break; case 'q': qflg++; break; default: usage(); } } if ((argc - optind) != 2) usage(); mnt.mnt_special = argv[optind]; mnt.mnt_mountp = argv[optind+1]; mnt.mnt_fstype = fstype; /* * Process options. The resulting options string overwrites the * original. * * XXX: This code doesn't do a good job of resolving options that are * specified multiple times or that are given in conflicting * forms (e.g., both "largefiles" and "nolargefiles"). It also * doesn't produce well defined behavior for options that may * also be specified as flags (e.g, "-r" and "ro"/"rw") when both * are present. * * The proper way to deal with such conflicts is to start with * the default value (i.e., the one if no flag or option is * specified), override it with the last mentioned option pair * in the -o option string, and finally, override that with * the flag value. This allows "mount -r" command to mount a * file system read only that is listed rw in /etc/vfstab. */ mnt.mnt_mntopts = opts; if (findopt(mnt.mnt_mntopts, "m")) mflg++; if ((gflg || findopt(mnt.mnt_mntopts, MNTOPT_GLOBAL)) && findopt(mnt.mnt_mntopts, MNTOPT_NBMAND)) { (void) fprintf(stderr, gettext("NBMAND option not supported on" " global filesystem\n")); exit(32); } replace_opts(opts, ro, MNTOPT_RO, MNTOPT_RW); replace_opts(opts, largefiles, MNTOPT_NOLARGEFILES, MNTOPT_LARGEFILES); gflg = replace_opts_dflt(opts, gflg, MNTOPT_GLOBAL, MNTOPT_NOGLOBAL); if (findopt(mnt.mnt_mntopts, MNTOPT_RQ)) { rmopt(&mnt, MNTOPT_RQ); replace_opts(opts, 1, MNTOPT_QUOTA, MNTOPT_NOQUOTA); } mountfs(&mnt); return (0); } static void reportlogerror(int ret, char *mp, char *special, char *cmd, fiolog_t *flp) { /* No error */ if ((ret != -1) && (flp->error == FIOLOG_ENONE)) return; /* logging was not enabled/disabled */ if (ret == -1 || flp->error != FIOLOG_ENONE) (void) fprintf(stderr, gettext("Could not %s logging" " for %s on %s.\n"), cmd, mp, special); /* ioctl returned error */ if (ret == -1) return; /* Some more info */ switch (flp->error) { case FIOLOG_ENONE : if (flp->nbytes_requested && (flp->nbytes_requested != flp->nbytes_actual)) { (void) fprintf(stderr, gettext("The log has been resized" " from %d bytes to %d bytes.\n"), flp->nbytes_requested, flp->nbytes_actual); } return; case FIOLOG_ETRANS : (void) fprintf(stderr, gettext("Solaris Volume Manager logging" " is already enabled.\n")); (void) fprintf(stderr, gettext("Please see the" " commands metadetach(1M)" " or metaclear(1M).\n")); break; case FIOLOG_EROFS : (void) fprintf(stderr, gettext("File system is mounted read " "only.\n")); (void) fprintf(stderr, gettext("Please see the remount " "option described in mount_ufs(1M).\n")); break; case FIOLOG_EULOCK : (void) fprintf(stderr, gettext("File system is locked.\n")); (void) fprintf(stderr, gettext("Please see the -u option " "described in lockfs(1M).\n")); break; case FIOLOG_EWLOCK : (void) fprintf(stderr, gettext("The file system could not be" " write locked.\n")); (void) fprintf(stderr, gettext("Please see the -w option " "described in lockfs(1M).\n")); break; case FIOLOG_ECLEAN : (void) fprintf(stderr, gettext("The file system may not be" " stable.\n")); (void) fprintf(stderr, gettext("Please see the -n option" " for fsck(1M).\n")); break; case FIOLOG_ENOULOCK : (void) fprintf(stderr, gettext("The file system could not be" " unlocked.\n")); (void) fprintf(stderr, gettext("Please see the -u option " "described in lockfs(1M).\n")); break; default : (void) fprintf(stderr, gettext("Unknown internal error" " %d.\n"), flp->error); break; } } static int checkislog(char *mp) { int fd; uint32_t islog; fd = open(mp, O_RDONLY); islog = 0; (void) ioctl(fd, _FIOISLOG, &islog); (void) close(fd); return ((int)islog); } static void enable_logging(char *mp, char *special) { int fd, ret, islog; fiolog_t fl; fd = open(mp, O_RDONLY); if (fd == -1) { perror(mp); return; } fl.nbytes_requested = 0; fl.nbytes_actual = 0; fl.error = FIOLOG_ENONE; ret = ioctl(fd, _FIOLOGENABLE, &fl); if (ret == -1) perror(mp); (void) close(fd); /* is logging enabled? */ islog = checkislog(mp); /* report errors, if any */ if (ret == -1 || !islog) reportlogerror(ret, mp, special, "enable", &fl); } static void disable_logging(char *mp, char *special) { int fd, ret, islog; fiolog_t fl; fd = open(mp, O_RDONLY); if (fd == -1) { perror(mp); return; } fl.error = FIOLOG_ENONE; ret = ioctl(fd, _FIOLOGDISABLE, &fl); if (ret == -1) perror(mp); (void) close(fd); /* is logging enabled? */ islog = checkislog(mp); /* report errors, if any */ if (ret == -1 || islog) reportlogerror(ret, mp, special, "disable", &fl); } /* * attempt to mount file system, return errno or 0 */ void mountfs(struct mnttab *mnt) { char opt[MAX_MNTOPT_STR]; char opt2[MAX_MNTOPT_STR]; char *opts = opt; int flags = MS_OPTIONSTR; struct ufs_args args; int need_separator = 0; int mount_attempts = 5; (void) bzero((char *)&args, sizeof (args)); (void) strcpy(opts, mnt->mnt_mntopts); opt2[0] = '\0'; flags |= Oflg ? MS_OVERLAY : 0; flags |= eatmntopt(mnt, MNTOPT_RO) ? MS_RDONLY : 0; flags |= eatmntopt(mnt, MNTOPT_REMOUNT) ? MS_REMOUNT : 0; flags |= eatmntopt(mnt, MNTOPT_GLOBAL) ? MS_GLOBAL : 0; if (eatmntopt(mnt, MNTOPT_NOINTR)) args.flags |= UFSMNT_NOINTR; if (eatmntopt(mnt, MNTOPT_INTR)) args.flags &= ~UFSMNT_NOINTR; if (eatmntopt(mnt, MNTOPT_SYNCDIR)) args.flags |= UFSMNT_SYNCDIR; if (eatmntopt(mnt, MNTOPT_FORCEDIRECTIO)) { args.flags |= UFSMNT_FORCEDIRECTIO; args.flags &= ~UFSMNT_NOFORCEDIRECTIO; } if (eatmntopt(mnt, MNTOPT_NOFORCEDIRECTIO)) { args.flags |= UFSMNT_NOFORCEDIRECTIO; args.flags &= ~UFSMNT_FORCEDIRECTIO; } if (eatmntopt(mnt, MNTOPT_NOSETSEC)) args.flags |= UFSMNT_NOSETSEC; if (eatmntopt(mnt, MNTOPT_LARGEFILES)) args.flags |= UFSMNT_LARGEFILES; if (eatmntopt(mnt, MNTOPT_NOLARGEFILES)) args.flags &= ~UFSMNT_LARGEFILES; args.flags |= UFSMNT_LOGGING; /* default is logging */ (void) eatmntopt(mnt, MNTOPT_LOGGING); if (eatmntopt(mnt, MNTOPT_NOLOGGING)) args.flags &= ~UFSMNT_LOGGING; if (eatmntopt(mnt, MNTOPT_NOATIME)) args.flags |= UFSMNT_NOATIME; if (eatmntopt(mnt, MNTOPT_DFRATIME)) args.flags &= ~UFSMNT_NODFRATIME; if (eatmntopt(mnt, MNTOPT_NODFRATIME)) args.flags |= UFSMNT_NODFRATIME; while (*opts != '\0') { char *argval; switch (getsubopt(&opts, fop_subopts, &argval)) { case ONERROR: if (argval) { struct fop_subopt *s; int found = 0; for (s = fop_subopt_list; s->str && !found; s++) { if (strcmp(argval, s->str) == 0) { args.flags |= s->flag; found = 1; } } if (!found) { usage(); } if (need_separator) (void) strcat(opt2, ","); (void) strcat(opt2, MNTOPT_ONERROR); (void) strcat(opt2, "="); (void) strcat(opt2, argval); need_separator = 1; } else { args.flags |= UFSMNT_ONERROR_DEFAULT; } break; case NOMATCH: default: if (argval) { if (need_separator) (void) strcat(opt2, ","); (void) strcat(opt2, argval); need_separator = 1; } break; } } if (*opt2 != '\0') (void) strcpy(opt, opt2); opts = opt; if ((args.flags & UFSMNT_ONERROR_FLGMASK) == 0) args.flags |= UFSMNT_ONERROR_DEFAULT; (void) signal(SIGHUP, SIG_IGN); (void) signal(SIGQUIT, SIG_IGN); (void) signal(SIGINT, SIG_IGN); errno = 0; flags |= MS_DATA | MS_OPTIONSTR; if (mflg) flags |= MS_NOMNTTAB; if (flags & MS_REMOUNT) { replace_opts(mnt->mnt_mntopts, 1, MNTOPT_RW, MNTOPT_RO); } fixopts(mnt, opts); /* * For global filesystems we want to pass in logging option * so that it shows up in the mnttab of all nodes. We add * logging option if its not specified. */ if (gflg || findopt(mnt->mnt_mntopts, MNTOPT_GLOBAL)) { if (mnt->mnt_mntopts != '\0') (void) strcat(mnt->mnt_mntopts, ","); (void) strcat(mnt->mnt_mntopts, MNTOPT_LOGGING); args.flags |= UFSMNT_LOGGING; } again: if (mount(mnt->mnt_special, mnt->mnt_mountp, flags, fstype, &args, sizeof (args), mnt->mnt_mntopts, MAX_MNTOPT_STR) != 0) { if (errno == EBUSY && !(flags & MS_OVERLAY)) { /* * Because of bug 6176743, any attempt to mount any * filesystem could fail for reasons described in that * bug. We're trying to detect that situation here by * checking that the filesystem we're mounting is not * in /etc/mnttab yet. When that bug is fixed, this * code can be removed. */ if (!in_mnttab(mnt->mnt_mountp) && mount_attempts-- > 0) { (void) poll(NULL, 0, 50); goto again; } } rpterr(mnt->mnt_special, mnt->mnt_mountp); exit(32); } if (!(flags & MS_RDONLY)) { if (args.flags & UFSMNT_LOGGING) enable_logging(mnt->mnt_mountp, mnt->mnt_special); else disable_logging(mnt->mnt_mountp, mnt->mnt_special); } if (!qflg) { cmp_requested_to_actual_options(opts, mnt->mnt_mntopts, mnt->mnt_special, mnt->mnt_mountp); } if (checkislog(mnt->mnt_mountp)) { /* update mnttab file if necessary */ if (!mflg) { struct stat64 statb; struct mnttagdesc mtdesc; int fd; if (stat64(mnt->mnt_mountp, &statb) != 0) exit(32); /* do tag ioctl */ mtdesc.mtd_major = major(statb.st_dev); mtdesc.mtd_minor = minor(statb.st_dev); mtdesc.mtd_mntpt = mnt->mnt_mountp; mtdesc.mtd_tag = MNTOPT_LOGGING; if ((fd = open(MNTTAB, O_RDONLY, 0)) < 0) exit(32); if (ioctl(fd, MNTIOC_SETTAG, &mtdesc) != 0) { (void) close(fd); exit(32); } (void) close(fd); } } exit(0); } /* * same as findopt but remove the option from the option string and return * true or false */ static int eatmntopt(struct mnttab *mnt, char *opt) { int has; has = (findopt(mnt->mnt_mntopts, opt) != NULL); rmopt(mnt, opt); return (has); } /* * remove an option string from the option list */ static void rmopt(struct mnttab *mnt, char *opt) { char *str; char *optstart; while (optstart = findopt(mnt->mnt_mntopts, opt)) { for (str = optstart; *str != ',' && *str != '\0' && *str != ' '; str++) /* NULL */; if (*str == ',') { str++; } else if (optstart != mnt->mnt_mntopts) { optstart--; } while (*optstart++ = *str++) ; } } /* * mnt->mnt_ops has un-eaten opts, opts is the original opts list. * Set mnt->mnt_opts to the original, the kernel will then remove * the ones it cannot deal with. * Set "opts" to the the original options for later comparison in * cmp_....(). But strip the options which aren't returned by * the kernel: "noglobal", "global" and "quota". * And strip the options which aren't set through mount: "logging", * "nologging" from those passed to mount(2). */ static void fixopts(struct mnttab *mnt, char *opts) { struct mnttab omnt; omnt.mnt_mntopts = opts; /* * Options not passed to the kernel and possibly not returned; * these are dealt with using ioctl; and the ioctl may fail. */ rmopt(&omnt, MNTOPT_LOGGING); rmopt(&omnt, MNTOPT_NOLOGGING); /* * Set the options for ``/etc/mnttab'' to be the original * options from main(); except for the option "f" and "remount". */ (void) strlcpy(mnt->mnt_mntopts, opts, MAX_MNTOPT_STR); rmopt(mnt, "f"); rmopt(mnt, MNTOPT_REMOUNT); rmopt(&omnt, MNTOPT_GLOBAL); rmopt(&omnt, MNTOPT_NOGLOBAL); rmopt(&omnt, MNTOPT_QUOTA); } static void usage(void) { (void) fprintf(stdout, gettext( "ufs usage:\n" "mount [-F ufs] [generic options] [-o suboptions] {special | mount_point}\n")); (void) fprintf(stdout, gettext( "\tsuboptions are: \n" "\t ro,rw,nosuid,remount,f,m,\n" "\t global,noglobal,\n" "\t largefiles,nolargefiles,\n" "\t forcedirectio,noforcedirectio\n" "\t logging,nologging,\n" "\t nbmand,nonbmand,\n" "\t onerror[={panic | lock | umount}]\n")); exit(32); } /* * Returns the next option in the option string. */ static char * getnextopt(char **p) { char *cp = *p; char *retstr; while (*cp && isspace(*cp)) cp++; retstr = cp; while (*cp && *cp != ',') cp++; /* strip empty options */ while (*cp == ',') { *cp = '\0'; cp++; } *p = cp; return (retstr); } /* * "trueopt" and "falseopt" are two settings of a Boolean option. * If "flag" is true, forcibly set the option to the "true" setting; otherwise, * if the option isn't present, set it to the false setting. */ static void replace_opts(char *options, int flag, char *trueopt, char *falseopt) { char *f; char *tmpoptsp; int found; char tmptopts[MNTMAXSTR]; (void) strcpy(tmptopts, options); tmpoptsp = tmptopts; (void) strcpy(options, ""); found = 0; for (f = getnextopt(&tmpoptsp); *f; f = getnextopt(&tmpoptsp)) { if (options[0] != '\0') (void) strcat(options, ","); if (strcmp(f, trueopt) == 0) { (void) strcat(options, f); found++; } else if (strcmp(f, falseopt) == 0) { if (flag) (void) strcat(options, trueopt); else (void) strcat(options, f); found++; } else (void) strcat(options, f); } if (!found) { if (options[0] != '\0') (void) strcat(options, ","); (void) strcat(options, flag ? trueopt : falseopt); } } /* * "trueopt" and "falseopt" are two settings of a Boolean option and "dflt" is * a default value for the option. Rewrite the contents of options to include * only the last mentioned occurrence of trueopt and falseopt. If neither is * mentioned, append one or the other to options, according to the value of * dflt. Return the resulting value of the option in boolean form. * * Note that the routine is implemented to have the resulting occurrence of * trueopt or falseopt appear at the end of the resulting option string. * * N.B. This routine should take the place of replace_opts, but there are * probably some compatibility issues to resolve before doing so. It * should certainly be used to handle new options that don't have * compatibility issues. */ static int replace_opts_dflt( char *options, int dflt, const char *trueopt, const char *falseopt) { char *f; char *tmpoptsp; int last; char tmptopts[MNTMAXSTR]; /* * Transfer the contents of options to tmptopts, in anticipation of * copying a subset of the contents back to options. */ (void) strcpy(tmptopts, options); tmpoptsp = tmptopts; (void) strcpy(options, ""); /* * Loop over each option value, copying non-matching values back into * options and updating the last seen occurrence of trueopt or * falseopt. */ last = dflt; for (f = getnextopt(&tmpoptsp); *f; f = getnextopt(&tmpoptsp)) { /* Check for both forms of the option of interest. */ if (strcmp(f, trueopt) == 0) { last = 1; } else if (strcmp(f, falseopt) == 0) { last = 0; } else { /* Not what we're looking for; transcribe. */ if (options[0] != '\0') (void) strcat(options, ","); (void) strcat(options, f); } } /* * Transcribe the correct form of the option of interest, using the * default value if it wasn't overwritten above. */ if (options[0] != '\0') (void) strcat(options, ","); (void) strcat(options, last ? trueopt : falseopt); return (last); } static void rpterr(char *bs, char *mp) { switch (errno) { case EPERM: (void) fprintf(stderr, gettext("%s: Insufficient privileges\n"), myname); break; case ENXIO: (void) fprintf(stderr, gettext("%s: %s no such device\n"), myname, bs); break; case ENOTDIR: (void) fprintf(stderr, gettext( "%s: %s not a directory\n\tor a component of %s is not a directory\n"), myname, mp, bs); break; case ENOENT: (void) fprintf(stderr, gettext( "%s: %s or %s, no such file or directory\n"), myname, bs, mp); break; case EINVAL: (void) fprintf(stderr, gettext("%s: %s is not this fstype\n"), myname, bs); break; case EBUSY: (void) fprintf(stderr, gettext("%s: %s is already mounted or %s is busy\n"), myname, bs, mp); break; case ENOTBLK: (void) fprintf(stderr, gettext( "%s: %s not a block device\n"), myname, bs); break; case EROFS: (void) fprintf(stderr, gettext("%s: %s write-protected\n"), myname, bs); break; case ENOSPC: (void) fprintf(stderr, gettext( "%s: The state of %s is not okay\n" "\tand it was attempted to be mounted read/write\n"), myname, bs); (void) printf(gettext( "mount: Please run fsck and try again\n")); break; case EFBIG: (void) fprintf(stderr, gettext( "%s: Large files may be present on %s,\n" "\tand it was attempted to be mounted nolargefiles\n"), myname, bs); break; default: perror(myname); (void) fprintf(stderr, gettext("%s: Cannot mount %s\n"), myname, bs); } }