xref: /illumos-gate/usr/src/lib/libc/port/gen/getmntent.c (revision 77c0a660417a046bfab6c8ef58d00c181c0264b3)
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
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 *
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
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
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
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
272 getmntent(FILE *fp, struct mnttab *mp)
273 {
274 	return (getmntent_common(fp, (struct extmnttab *)mp, MNTIOC_GETMNTENT));
275 }
276 
277 int
278 getextmntent(FILE *fp, struct extmnttab *emp, size_t len __unused)
279 {
280 	return (getmntent_common(fp, emp, MNTIOC_GETEXTMNTENT));
281 }
282 
283 char *
284 mntopt(char **p)
285 {
286 	char *cp = *p;
287 	char *retstr;
288 
289 	while (*cp && isspace(*cp))
290 		cp++;
291 
292 	retstr = cp;
293 	while (*cp && *cp != ',')
294 		cp++;
295 
296 	if (*cp) {
297 		*cp = '\0';
298 		cp++;
299 	}
300 
301 	*p = cp;
302 	return (retstr);
303 }
304 
305 char *
306 hasmntopt(struct mnttab *mnt, char *opt)
307 {
308 	char tmpopts[MNT_LINE_MAX];
309 	char *f, *opts = tmpopts;
310 	size_t	len;
311 
312 	if (mnt->mnt_mntopts == NULL)
313 		return (NULL);
314 	(void) strcpy(opts, mnt->mnt_mntopts);
315 	len = strlen(opt);
316 	f = mntopt(&opts);
317 	for (; *f; f = mntopt(&opts)) {
318 		/*
319 		 * Match only complete substrings. For options
320 		 * which use a delimiter (such as 'retry=3'),
321 		 * treat the delimiter as the end of the substring.
322 		 */
323 		if (strncmp(opt, f, len) == 0 &&
324 		    (f[len] == '\0' || !isalnum(f[len])))
325 			return (f - tmpopts + mnt->mnt_mntopts);
326 	}
327 	return (NULL);
328 }
329 
330 void
331 resetmnttab(FILE *fp)
332 {
333 	rewind(fp);
334 }
335 
336 /*
337  * Compatibility for non-mntfs files.  For backwards compatibility, we continue
338  * to have to support this broken interface.  Note that getextmntent() has
339  * always failed when using a file other than /etc/mnttab, because it relies on
340  * an ioctl() call.
341  */
342 static int
343 getaline(char *lp, FILE *fp)
344 {
345 	char	*cp;
346 
347 	while ((lp = fgets(lp, MNT_LINE_MAX, fp)) != NULL) {
348 		if (strlen(lp) == MNT_LINE_MAX-1 && lp[MNT_LINE_MAX-2] != '\n')
349 			return (MNT_TOOLONG);
350 
351 		for (cp = lp; *cp == ' ' || *cp == '\t'; cp++)
352 			;
353 
354 		if (*cp != '#' && *cp != '\n')
355 			return (0);
356 	}
357 	return (-1);
358 }
359 
360 static int
361 getmntent_compat(FILE *fp, struct mnttab *mp)
362 {
363 	int	ret;
364 	char	*tmp;
365 	char	*line = getmntbuf(MNT_LINE_MAX);
366 
367 	if (line == NULL) {
368 		errno = ENOMEM;
369 		return (-1);
370 	}
371 
372 	/* skip leading spaces and comments */
373 	if ((ret = getaline(line, fp)) != 0)
374 		return (ret);
375 
376 	/* split up each field */
377 	GETTOK_R(mnt_special, line, &tmp);
378 	GETTOK_R(mnt_mountp, NULL, &tmp);
379 	GETTOK_R(mnt_fstype, NULL, &tmp);
380 	GETTOK_R(mnt_mntopts, NULL, &tmp);
381 	GETTOK_R(mnt_time, NULL, &tmp);
382 
383 	/* check for too many fields */
384 	if (strtok_r(NULL, sepstr, &tmp) != NULL)
385 		return (MNT_TOOMANY);
386 
387 	return (0);
388 }
389