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