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 (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 /* Copyright (c) 1988 AT&T */
27 /* All Rights Reserved */
28
29 #include "lint.h"
30 #include <mtlib.h>
31 #include <stdio.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <sys/mnttab.h>
35 #include <sys/mntio.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <errno.h>
39 #include <stdlib.h>
40 #include <thread.h>
41 #include <synch.h>
42 #include <libc.h>
43 #include <unistd.h>
44 #include "tsd.h"
45 #include <atomic.h>
46 #include <strings.h>
47
48 static int getmntent_compat(FILE *fp, struct mnttab *mp);
49
50 #define GETTOK_R(xx, ll, tmp)\
51 if ((mp->xx = (char *)strtok_r(ll, sepstr, tmp)) == NULL)\
52 return (MNT_TOOFEW);\
53 if (strcmp(mp->xx, dash) == 0)\
54 mp->xx = NULL
55
56 #define DIFF(xx)\
57 (mrefp->xx != NULL && (mgetp->xx == NULL ||\
58 strcmp(mrefp->xx, mgetp->xx) != 0))
59
60 #define SDIFF(xx, typem, typer)\
61 ((mgetp->xx == NULL) || (stat64(mgetp->xx, &statb) == -1) ||\
62 ((statb.st_mode & S_IFMT) != typem) ||\
63 (statb.st_rdev != typer))
64
65 static const char sepstr[] = " \t\n";
66 static const char dash[] = "-";
67
68 typedef struct {
69 size_t buflen;
70 char *buf;
71 } thread_data_t;
72
73 static void
destroy_thread_data(void * arg)74 destroy_thread_data(void *arg)
75 {
76 thread_data_t *thread_data = arg;
77
78 if (thread_data->buf != NULL) {
79 free(thread_data->buf);
80 thread_data->buf = NULL;
81 }
82 thread_data->buflen = 0;
83 }
84
85 static char *
getmntbuf(size_t size)86 getmntbuf(size_t size)
87 {
88 thread_data_t *thread_data;
89
90 thread_data = tsdalloc(_T_GETMNTENT,
91 sizeof (thread_data_t), destroy_thread_data);
92 if (thread_data == NULL)
93 return (NULL);
94 if (thread_data->buf == NULL ||
95 thread_data->buflen < size) {
96 if (thread_data->buf != NULL)
97 free(thread_data->buf);
98 thread_data->buflen = 0;
99 if ((thread_data->buf = malloc(size)) == NULL)
100 return (NULL);
101 thread_data->buflen = size;
102 }
103 return (thread_data->buf);
104 }
105
106 static int
getmntany_compat(FILE * fp,struct mnttab * mgetp,struct mnttab * mrefp)107 getmntany_compat(FILE *fp, struct mnttab *mgetp, struct mnttab *mrefp)
108 {
109 int ret, bstat;
110 mode_t bmode;
111 dev_t brdev;
112 struct stat64 statb;
113
114 /*
115 * Ignore specials that don't correspond to real devices to avoid doing
116 * unnecessary lookups in stat64().
117 */
118 if (mrefp->mnt_special && mrefp->mnt_special[0] == '/' &&
119 stat64(mrefp->mnt_special, &statb) == 0 &&
120 ((bmode = (statb.st_mode & S_IFMT)) == S_IFBLK ||
121 bmode == S_IFCHR)) {
122 bstat = 1;
123 brdev = statb.st_rdev;
124 } else {
125 bstat = 0;
126 }
127
128 while ((ret = getmntent_compat(fp, mgetp)) == 0 &&
129 ((bstat == 0 && DIFF(mnt_special)) ||
130 (bstat == 1 && SDIFF(mnt_special, bmode, brdev)) ||
131 DIFF(mnt_mountp) ||
132 DIFF(mnt_fstype) ||
133 DIFF(mnt_mntopts) ||
134 DIFF(mnt_time)))
135 ;
136
137 return (ret);
138 }
139
140 int
getmntany(FILE * fp,struct mnttab * mgetp,struct mnttab * mrefp)141 getmntany(FILE *fp, struct mnttab *mgetp, struct mnttab *mrefp)
142 {
143 struct mntentbuf embuf;
144 char *copyp, *bufp;
145 int ret;
146
147
148 /*
149 * We collect all of the text strings pointed to by members of the
150 * user's preferences struct into a single buffer. At the same time
151 * populate the members of the results struct to point to the
152 * corresponding words. We then ask the kernel to figure out the
153 * rest; if this is a non-mntfs file then we handover to
154 * getmntany_compat().
155 */
156 if ((copyp = bufp = getmntbuf(MNT_LINE_MAX)) == NULL) {
157 errno = ENOMEM;
158 return (-1);
159 }
160 bzero(mgetp, sizeof (struct mnttab));
161 if (mrefp->mnt_special) {
162 mgetp->mnt_special = copyp;
163 copyp += snprintf(mgetp->mnt_special, MNT_LINE_MAX, "%s",
164 mrefp->mnt_special) + 1;
165 }
166 if (mrefp->mnt_mountp) {
167 mgetp->mnt_mountp = copyp;
168 copyp += snprintf(mgetp->mnt_mountp,
169 bufp + MNT_LINE_MAX - copyp, "%s", mrefp->mnt_mountp) + 1;
170 }
171 if (mrefp->mnt_fstype) {
172 mgetp->mnt_fstype = copyp;
173 copyp += snprintf(mgetp->mnt_fstype,
174 bufp + MNT_LINE_MAX - copyp, "%s", mrefp->mnt_fstype) + 1;
175 }
176 if (mrefp->mnt_mntopts) {
177 mgetp->mnt_mntopts = copyp;
178 copyp += snprintf(mgetp->mnt_mntopts,
179 bufp + MNT_LINE_MAX - copyp, "%s", mrefp->mnt_mntopts) + 1;
180 }
181 if (mrefp->mnt_time) {
182 mgetp->mnt_time = copyp;
183 (void) snprintf(mgetp->mnt_time, bufp + MNT_LINE_MAX - copyp,
184 "%s", mrefp->mnt_time);
185 }
186
187 embuf.mbuf_emp = (struct extmnttab *)mgetp;
188 embuf.mbuf_bufsize = MNT_LINE_MAX;
189 embuf.mbuf_buf = bufp;
190
191 switch (ret = ioctl(fileno(fp), MNTIOC_GETMNTANY, &embuf)) {
192 case 0:
193 /* Success. */
194 return (0);
195 case MNTFS_EOF:
196 return (-1);
197 case MNTFS_TOOLONG:
198 return (MNT_TOOLONG);
199 default:
200 /* A failure of some kind. */
201 if (errno == ENOTTY)
202 return (getmntany_compat(fp, mgetp, mrefp));
203 else
204 return (ret);
205 }
206 }
207
208 /*
209 * Common code for getmntent() and getextmntent().
210 *
211 * These functions serve to populate a structure supplied by the user. Common
212 * to both struct mnttab and struct extmnttab is a set of pointers to the
213 * individual text fields that form an entry in /etc/mnttab. We arrange for the
214 * text itself to be stored in some thread-local storage, and for the kernel to
215 * populate both this buffer and the structure directly.
216 *
217 * If getmntent() passes a file that isn't provided by mntfs then we assume that
218 * it is a simple text file and give it to getmntent_compat() to parse. For
219 * getextmntent() we give up; it requires major and minor numbers that only the
220 * kernel can provide.
221 */
222 static int
getmntent_common(FILE * fp,struct extmnttab * emp,int command)223 getmntent_common(FILE *fp, struct extmnttab *emp, int command)
224 {
225 struct mntentbuf embuf;
226 static size_t bufsize = MNT_LINE_MAX;
227 int ret;
228
229 embuf.mbuf_emp = emp;
230 embuf.mbuf_bufsize = bufsize;
231 if ((embuf.mbuf_buf = getmntbuf(embuf.mbuf_bufsize)) == NULL) {
232 errno = ENOMEM;
233 return (-1);
234 }
235
236 while ((ret = ioctl(fileno(fp), command, &embuf)) == MNTFS_TOOLONG) {
237 /* The buffer wasn't large enough. */
238 (void) atomic_swap_ulong((unsigned long *)&bufsize,
239 2 * embuf.mbuf_bufsize);
240 embuf.mbuf_bufsize = bufsize;
241 if ((embuf.mbuf_buf = getmntbuf(embuf.mbuf_bufsize)) == NULL) {
242 errno = ENOMEM;
243 return (-1);
244 }
245 }
246
247 switch (ret) {
248 case 0:
249 /*
250 * We were successful, but we may have to enforce getmntent()'s
251 * documented limit on the line length.
252 */
253 if (command == MNTIOC_GETMNTENT &&
254 (emp->mnt_time + strlen(emp->mnt_time) + 1 -
255 emp->mnt_special > MNT_LINE_MAX))
256 return (MNT_TOOLONG);
257 else
258 return (0);
259 case MNTFS_EOF:
260 /* EOF. */
261 return (-1);
262 default:
263 /* A non-mntfs file. */
264 if (command == MNTIOC_GETMNTENT)
265 return (getmntent_compat(fp, (struct mnttab *)emp));
266 else
267 return (ret);
268 }
269 }
270
271 int
getmntent(FILE * fp,struct mnttab * mp)272 getmntent(FILE *fp, struct mnttab *mp)
273 {
274 return (getmntent_common(fp, (struct extmnttab *)mp, MNTIOC_GETMNTENT));
275 }
276
277 /*ARGSUSED*/
278 int
getextmntent(FILE * fp,struct extmnttab * emp,size_t len)279 getextmntent(FILE *fp, struct extmnttab *emp, size_t len)
280 {
281 return (getmntent_common(fp, emp, MNTIOC_GETEXTMNTENT));
282 }
283
284 char *
mntopt(char ** p)285 mntopt(char **p)
286 {
287 char *cp = *p;
288 char *retstr;
289
290 while (*cp && isspace(*cp))
291 cp++;
292
293 retstr = cp;
294 while (*cp && *cp != ',')
295 cp++;
296
297 if (*cp) {
298 *cp = '\0';
299 cp++;
300 }
301
302 *p = cp;
303 return (retstr);
304 }
305
306 char *
hasmntopt(struct mnttab * mnt,char * opt)307 hasmntopt(struct mnttab *mnt, char *opt)
308 {
309 char tmpopts[MNT_LINE_MAX];
310 char *f, *opts = tmpopts;
311 size_t len;
312
313 if (mnt->mnt_mntopts == NULL)
314 return (NULL);
315 (void) strcpy(opts, mnt->mnt_mntopts);
316 len = strlen(opt);
317 f = mntopt(&opts);
318 for (; *f; f = mntopt(&opts)) {
319 /*
320 * Match only complete substrings. For options
321 * which use a delimiter (such as 'retry=3'),
322 * treat the delimiter as the end of the substring.
323 */
324 if (strncmp(opt, f, len) == 0 &&
325 (f[len] == '\0' || !isalnum(f[len])))
326 return (f - tmpopts + mnt->mnt_mntopts);
327 }
328 return (NULL);
329 }
330
331 void
resetmnttab(FILE * fp)332 resetmnttab(FILE *fp)
333 {
334 rewind(fp);
335 }
336
337 /*
338 * Compatibility for non-mntfs files. For backwards compatibility, we continue
339 * to have to support this broken interface. Note that getextmntent() has
340 * always failed when using a file other than /etc/mnttab, because it relies on
341 * an ioctl() call.
342 */
343 static int
getaline(char * lp,FILE * fp)344 getaline(char *lp, FILE *fp)
345 {
346 char *cp;
347
348 while ((lp = fgets(lp, MNT_LINE_MAX, fp)) != NULL) {
349 if (strlen(lp) == MNT_LINE_MAX-1 && lp[MNT_LINE_MAX-2] != '\n')
350 return (MNT_TOOLONG);
351
352 for (cp = lp; *cp == ' ' || *cp == '\t'; cp++)
353 ;
354
355 if (*cp != '#' && *cp != '\n')
356 return (0);
357 }
358 return (-1);
359 }
360
361 static int
getmntent_compat(FILE * fp,struct mnttab * mp)362 getmntent_compat(FILE *fp, struct mnttab *mp)
363 {
364 int ret;
365 char *tmp;
366 char *line = getmntbuf(MNT_LINE_MAX);
367
368 if (line == NULL) {
369 errno = ENOMEM;
370 return (-1);
371 }
372
373 /* skip leading spaces and comments */
374 if ((ret = getaline(line, fp)) != 0)
375 return (ret);
376
377 /* split up each field */
378 GETTOK_R(mnt_special, line, &tmp);
379 GETTOK_R(mnt_mountp, NULL, &tmp);
380 GETTOK_R(mnt_fstype, NULL, &tmp);
381 GETTOK_R(mnt_mntopts, NULL, &tmp);
382 GETTOK_R(mnt_time, NULL, &tmp);
383
384 /* check for too many fields */
385 if (strtok_r(NULL, sepstr, &tmp) != NULL)
386 return (MNT_TOOMANY);
387
388 return (0);
389 }
390