1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <sys/types.h> 28 #include <locale.h> 29 #include <stdio.h> 30 #include <time.h> 31 #include <signal.h> 32 #include <string.h> 33 #include <unistd.h> 34 #include <stdlib.h> 35 #include <errno.h> 36 #include <sys/mntent.h> 37 #include <sys/mnttab.h> 38 #include <sys/mount.h> 39 #include <sys/fs/pc_fs.h> 40 #include <fslib.h> 41 42 extern int daylight; 43 44 static int roflag = 0; 45 static char optbuf[MAX_MNTOPT_STR] = { '\0', }; 46 static int optsize = 0; 47 48 49 /* 50 * Since the format/value expected for the mount options listed below 51 * differs between what the user mount command expects and what the 52 * kernel module can grok, we transmogrify the mount option string 53 * for such options. Others are copied through as-is. 54 */ 55 static char *pcfs_opts[] = { MNTOPT_PCFS_TIMEZONE, NULL }; 56 #define ARG_PCFS_TIMEZONE 0 57 58 /* 59 * While constructing the mount option string, we need to append 60 * comma separators if there have been previous options copied over 61 * from the input string. This takes care of it. 62 */ 63 static int 64 append_opt(char *str, int strsz, char *opt) 65 { 66 if (str[0] != '\0' && strlcat(str, ",", strsz) >= strsz) 67 return (0); 68 69 return (strlcat(str, opt, strsz) < strsz); 70 } 71 72 int 73 main(int argc, char *argv[]) 74 { 75 char *mnt_special; 76 char *mnt_mountp; 77 int c; 78 char *myname; 79 char typename[64]; 80 char tzstr[100]; 81 char *tzval; 82 char *savedoptbuf = NULL, *savedoptarg = NULL; 83 char *in_arg, *val, *curarg; 84 extern int optind; 85 extern char *optarg; 86 int error = 0; 87 int verbose = 0; 88 int mflg = MS_OPTIONSTR; /* we always pass mount options */ 89 int qflg = 0; 90 int optcnt = 0; 91 int tzdone = 0; 92 93 myname = strrchr(argv[0], '/'); 94 myname = myname ? myname + 1 : argv[0]; 95 (void) snprintf(typename, sizeof (typename), "%s_%s", 96 MNTTYPE_PCFS, myname); 97 argv[0] = typename; 98 99 while ((c = getopt(argc, argv, "Vvmr?o:Oq")) != EOF) { 100 switch (c) { 101 case 'V': 102 case 'v': 103 verbose++; 104 break; 105 case '?': 106 error++; 107 break; 108 case 'r': 109 roflag++; 110 break; 111 case 'm': 112 mflg |= MS_NOMNTTAB; 113 break; 114 case 'o': 115 in_arg = optarg; 116 if ((savedoptarg = strdup(optarg)) == NULL) { 117 (void) fprintf(stderr, 118 gettext("%s: out of memory\n"), myname); 119 exit(2); 120 } 121 while (*in_arg != '\0') { 122 curarg = in_arg; 123 124 switch (getsubopt(&in_arg, pcfs_opts, &val)) { 125 case ARG_PCFS_TIMEZONE: 126 if (tzdone || val == NULL) 127 goto invalarg; 128 tzval = val; 129 (void) snprintf(tzstr, 100, 130 "TZ=%s", tzval); 131 tzstr[99] = '\0'; 132 (void) putenv(tzstr); 133 tzdone = 1; 134 break; 135 default: 136 /* 137 * Remove empty suboptions 138 * (happens on sequences of commas) 139 */ 140 if (*curarg == '\0') 141 break; 142 143 if (append_opt(optbuf, sizeof (optbuf), 144 curarg) == 0) 145 goto invalarg; 146 } 147 } 148 break; 149 case 'O': 150 mflg |= MS_OVERLAY; 151 break; 152 case 'q': 153 qflg = 1; 154 break; 155 } 156 } 157 158 if (verbose && !error) { 159 char *optptr; 160 161 (void) fprintf(stderr, "%s", typename); 162 for (optcnt = 1; optcnt < argc; optcnt++) { 163 optptr = argv[optcnt]; 164 if (optptr) 165 (void) fprintf(stderr, " %s", optptr); 166 } 167 (void) fprintf(stderr, "\n"); 168 } 169 170 if (argc - optind != 2 || error) { 171 /* 172 * don't hint at options yet (none are really supported) 173 */ 174 (void) fprintf(stderr, gettext( 175 "Usage: %s [generic options] [-o suboptions] " 176 "special mount_point\n"), typename); 177 (void) fprintf(stderr, gettext( 178 "\tpcfs-specific suboptions are:\n" 179 "\t clamptime,noclamptime\n" 180 "\t hidden,nohidden\n" 181 "\t atime,noatime\n" 182 "\t foldcase,nofoldcase\n" 183 "\t timezone=<valid TZ string>")); 184 exit(32); 185 } 186 187 mnt_special = argv[optind++]; 188 mnt_mountp = argv[optind++]; 189 190 /* 191 * Pass timezone information to the kernel module so that 192 * FAT timestamps, as per spec, can be recorded in local time. 193 */ 194 tzset(); 195 /* 196 * We perform this validation only in case the user of 197 * mount(8) specified the "timezone=..." option. That's 198 * because we don't want PCFS mounts to fail due to a 199 * botched $TZ environment variable. If the admin's 200 * environment contains garbage, it'll just parse as 201 * GMT (timezone=0). 202 */ 203 if (tzdone && timezone == 0 && altzone == 0 && daylight == 0 && 204 strcmp(tzname[0], tzval) && 205 strspn(tzname[1], " ") == strlen(tzname[1])) { 206 goto invalarg; 207 } 208 (void) snprintf(tzstr, 100, "timezone=%d", timezone); 209 tzstr[99] = '\0'; 210 if (append_opt(optbuf, sizeof (optbuf), tzstr) == 0) 211 goto invalarg; 212 213 optsize = strlen(optbuf); 214 215 if (roflag) 216 mflg |= MS_RDONLY; 217 218 if ((savedoptbuf = strdup(optbuf)) == NULL) { 219 (void) fprintf(stderr, gettext("%s: out of memory\n"), 220 myname); 221 exit(2); 222 } 223 (void) signal(SIGHUP, SIG_IGN); 224 (void) signal(SIGQUIT, SIG_IGN); 225 (void) signal(SIGINT, SIG_IGN); 226 227 if (verbose) { 228 (void) fprintf(stderr, "mount(%s, \"%s\", %d, %s", 229 mnt_special, mnt_mountp, mflg, MNTTYPE_PCFS); 230 } 231 if (mount(mnt_special, mnt_mountp, mflg, MNTTYPE_PCFS, 232 NULL, 0, optbuf, MAX_MNTOPT_STR)) { 233 if (errno == EBUSY) { 234 (void) fprintf(stderr, gettext( 235 "mount: %s is already mounted or %s is busy\n"), 236 mnt_special, mnt_mountp); 237 } else if (errno == EINVAL) { 238 (void) fprintf(stderr, gettext( 239 "mount: %s is not a DOS filesystem.\n"), 240 mnt_special); 241 } else { 242 perror("mount"); 243 } 244 exit(32); 245 } 246 247 if (optsize && !qflg) 248 cmp_requested_to_actual_options(savedoptbuf, optbuf, 249 mnt_special, mnt_mountp); 250 return (0); 251 252 invalarg: 253 (void) fprintf(stderr, 254 gettext("%s: Invalid mount options: %s\n"), myname, savedoptarg); 255 return (2); 256 } 257