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
append_opt(char * str,int strsz,char * opt)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
main(int argc,char * argv[])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