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