1 /*
2  * Copyright (c) 2006 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 /*
28  * This file implements Solaris compatible getmntany() and hasmntopt()
29  * functions.
30  */
31 
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34 
35 #include <sys/param.h>
36 #include <sys/mount.h>
37 #include <sys/mntent.h>
38 #include <sys/mnttab.h>
39 
40 #include <ctype.h>
41 #include <errno.h>
42 #include <pthread.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 
47 static char *
48 mntopt(char **p)
49 {
50 	char *cp = *p;
51 	char *retstr;
52 
53 	while (*cp && isspace(*cp))
54 		cp++;
55 
56 	retstr = cp;
57 	while (*cp && *cp != ',')
58 		cp++;
59 
60 	if (*cp) {
61 		*cp = '\0';
62 		cp++;
63 	}
64 
65 	*p = cp;
66 	return (retstr);
67 }
68 
69 char *
70 hasmntopt(struct mnttab *mnt, const char *opt)
71 {
72 	char tmpopts[MNT_LINE_MAX];
73 	char *f, *opts = tmpopts;
74 
75 	if (mnt->mnt_mntopts == NULL)
76 		return (NULL);
77 	(void) strlcpy(opts, mnt->mnt_mntopts, MNT_LINE_MAX);
78 	f = mntopt(&opts);
79 	for (; *f; f = mntopt(&opts)) {
80 		if (strncmp(opt, f, strlen(opt)) == 0)
81 			return (f - tmpopts + mnt->mnt_mntopts);
82 	}
83 	return (NULL);
84 }
85 
86 static void
87 optadd(char *mntopts, size_t size, const char *opt)
88 {
89 
90 	if (mntopts[0] != '\0')
91 		strlcat(mntopts, ",", size);
92 	strlcat(mntopts, opt, size);
93 }
94 
95 static __thread char gfstypename[MFSNAMELEN];
96 static __thread char gmntfromname[MNAMELEN];
97 static __thread char gmntonname[MNAMELEN];
98 static __thread char gmntopts[MNTMAXSTR];
99 
100 void
101 statfs2mnttab(struct statfs *sfs, struct mnttab *mp)
102 {
103 	long flags;
104 
105 	strlcpy(gfstypename, sfs->f_fstypename, sizeof (gfstypename));
106 	mp->mnt_fstype = gfstypename;
107 
108 	strlcpy(gmntfromname, sfs->f_mntfromname, sizeof (gmntfromname));
109 	mp->mnt_special = gmntfromname;
110 
111 	strlcpy(gmntonname, sfs->f_mntonname, sizeof (gmntonname));
112 	mp->mnt_mountp = gmntonname;
113 
114 	flags = sfs->f_flags;
115 	gmntopts[0] = '\0';
116 #define	OPTADD(opt)	optadd(gmntopts, sizeof (gmntopts), (opt))
117 	if (flags & MNT_RDONLY)
118 		OPTADD(MNTOPT_RO);
119 	else
120 		OPTADD(MNTOPT_RW);
121 	if (flags & MNT_NOSUID)
122 		OPTADD(MNTOPT_NOSETUID);
123 	else
124 		OPTADD(MNTOPT_SETUID);
125 	if (flags & MNT_UPDATE)
126 		OPTADD(MNTOPT_REMOUNT);
127 	if (flags & MNT_NOATIME)
128 		OPTADD(MNTOPT_NOATIME);
129 	else
130 		OPTADD(MNTOPT_ATIME);
131 	OPTADD(MNTOPT_NOXATTR);
132 	if (flags & MNT_NOEXEC)
133 		OPTADD(MNTOPT_NOEXEC);
134 	else
135 		OPTADD(MNTOPT_EXEC);
136 #undef	OPTADD
137 	mp->mnt_mntopts = gmntopts;
138 }
139 
140 static pthread_rwlock_t gsfs_lock = PTHREAD_RWLOCK_INITIALIZER;
141 static struct statfs *gsfs = NULL;
142 static int allfs = 0;
143 
144 static int
145 statfs_init(void)
146 {
147 	struct statfs *sfs;
148 	int error;
149 
150 	(void) pthread_rwlock_wrlock(&gsfs_lock);
151 
152 	if (gsfs != NULL) {
153 		free(gsfs);
154 		gsfs = NULL;
155 	}
156 	allfs = getfsstat(NULL, 0, MNT_NOWAIT);
157 	if (allfs == -1)
158 		goto fail;
159 	gsfs = malloc(sizeof (gsfs[0]) * allfs * 2);
160 	if (gsfs == NULL)
161 		goto fail;
162 	allfs = getfsstat(gsfs, (long)(sizeof (gsfs[0]) * allfs * 2),
163 	    MNT_NOWAIT);
164 	if (allfs == -1)
165 		goto fail;
166 	sfs = realloc(gsfs, allfs * sizeof (gsfs[0]));
167 	if (sfs != NULL)
168 		gsfs = sfs;
169 	(void) pthread_rwlock_unlock(&gsfs_lock);
170 	return (0);
171 fail:
172 	error = errno;
173 	if (gsfs != NULL)
174 		free(gsfs);
175 	gsfs = NULL;
176 	allfs = 0;
177 	(void) pthread_rwlock_unlock(&gsfs_lock);
178 	return (error);
179 }
180 
181 int
182 getmntany(FILE *fd __unused, struct mnttab *mgetp, struct mnttab *mrefp)
183 {
184 	int i, error;
185 
186 	error = statfs_init();
187 	if (error != 0)
188 		return (error);
189 
190 	(void) pthread_rwlock_rdlock(&gsfs_lock);
191 
192 	for (i = 0; i < allfs; i++) {
193 		if (mrefp->mnt_special != NULL &&
194 		    strcmp(mrefp->mnt_special, gsfs[i].f_mntfromname) != 0) {
195 			continue;
196 		}
197 		if (mrefp->mnt_mountp != NULL &&
198 		    strcmp(mrefp->mnt_mountp, gsfs[i].f_mntonname) != 0) {
199 			continue;
200 		}
201 		if (mrefp->mnt_fstype != NULL &&
202 		    strcmp(mrefp->mnt_fstype, gsfs[i].f_fstypename) != 0) {
203 			continue;
204 		}
205 		statfs2mnttab(&gsfs[i], mgetp);
206 		(void) pthread_rwlock_unlock(&gsfs_lock);
207 		return (0);
208 	}
209 	(void) pthread_rwlock_unlock(&gsfs_lock);
210 	return (-1);
211 }
212 
213 int
214 getmntent(FILE *fp, struct mnttab *mp)
215 {
216 	int error, nfs;
217 
218 	nfs = (int)lseek(fileno(fp), 0, SEEK_CUR);
219 	if (nfs == -1)
220 		return (errno);
221 	/* If nfs is 0, we want to refresh out cache. */
222 	if (nfs == 0 || gsfs == NULL) {
223 		error = statfs_init();
224 		if (error != 0)
225 			return (error);
226 	}
227 	(void) pthread_rwlock_rdlock(&gsfs_lock);
228 	if (nfs >= allfs) {
229 		(void) pthread_rwlock_unlock(&gsfs_lock);
230 		return (-1);
231 	}
232 	statfs2mnttab(&gsfs[nfs], mp);
233 	(void) pthread_rwlock_unlock(&gsfs_lock);
234 	if (lseek(fileno(fp), 1, SEEK_CUR) == -1)
235 		return (errno);
236 	return (0);
237 }
238