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/param.h>
33 #include <sys/mount.h>
34 #include <sys/mntent.h>
35 #include <sys/mnttab.h>
36 
37 #include <ctype.h>
38 #include <errno.h>
39 #include <pthread.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 
44 static char *
45 mntopt(char **p)
46 {
47 	char *cp = *p;
48 	char *retstr;
49 
50 	while (*cp && isspace(*cp))
51 		cp++;
52 
53 	retstr = cp;
54 	while (*cp && *cp != ',')
55 		cp++;
56 
57 	if (*cp) {
58 		*cp = '\0';
59 		cp++;
60 	}
61 
62 	*p = cp;
63 	return (retstr);
64 }
65 
66 char *
67 hasmntopt(struct mnttab *mnt, const char *opt)
68 {
69 	char tmpopts[MNT_LINE_MAX];
70 	char *f, *opts = tmpopts;
71 
72 	if (mnt->mnt_mntopts == NULL)
73 		return (NULL);
74 	(void) strlcpy(opts, mnt->mnt_mntopts, MNT_LINE_MAX);
75 	f = mntopt(&opts);
76 	for (; *f; f = mntopt(&opts)) {
77 		if (strncmp(opt, f, strlen(opt)) == 0)
78 			return (f - tmpopts + mnt->mnt_mntopts);
79 	}
80 	return (NULL);
81 }
82 
83 static void
84 optadd(char *mntopts, size_t size, const char *opt)
85 {
86 
87 	if (mntopts[0] != '\0')
88 		strlcat(mntopts, ",", size);
89 	strlcat(mntopts, opt, size);
90 }
91 
92 static __thread char gfstypename[MFSNAMELEN];
93 static __thread char gmntfromname[MNAMELEN];
94 static __thread char gmntonname[MNAMELEN];
95 static __thread char gmntopts[MNTMAXSTR];
96 
97 void
98 statfs2mnttab(struct statfs *sfs, struct mnttab *mp)
99 {
100 	long flags;
101 
102 	strlcpy(gfstypename, sfs->f_fstypename, sizeof (gfstypename));
103 	mp->mnt_fstype = gfstypename;
104 
105 	strlcpy(gmntfromname, sfs->f_mntfromname, sizeof (gmntfromname));
106 	mp->mnt_special = gmntfromname;
107 
108 	strlcpy(gmntonname, sfs->f_mntonname, sizeof (gmntonname));
109 	mp->mnt_mountp = gmntonname;
110 
111 	flags = sfs->f_flags;
112 	gmntopts[0] = '\0';
113 #define	OPTADD(opt)	optadd(gmntopts, sizeof (gmntopts), (opt))
114 	if (flags & MNT_RDONLY)
115 		OPTADD(MNTOPT_RO);
116 	else
117 		OPTADD(MNTOPT_RW);
118 	if (flags & MNT_NOSUID)
119 		OPTADD(MNTOPT_NOSETUID);
120 	else
121 		OPTADD(MNTOPT_SETUID);
122 	if (flags & MNT_UPDATE)
123 		OPTADD(MNTOPT_REMOUNT);
124 	if (flags & MNT_NOATIME)
125 		OPTADD(MNTOPT_NOATIME);
126 	else
127 		OPTADD(MNTOPT_ATIME);
128 	OPTADD(MNTOPT_NOXATTR);
129 	if (flags & MNT_NOEXEC)
130 		OPTADD(MNTOPT_NOEXEC);
131 	else
132 		OPTADD(MNTOPT_EXEC);
133 #undef	OPTADD
134 	mp->mnt_mntopts = gmntopts;
135 }
136 
137 static pthread_rwlock_t gsfs_lock = PTHREAD_RWLOCK_INITIALIZER;
138 static struct statfs *gsfs = NULL;
139 static int allfs = 0;
140 
141 static int
142 statfs_init(void)
143 {
144 	struct statfs *sfs;
145 	int error;
146 
147 	(void) pthread_rwlock_wrlock(&gsfs_lock);
148 
149 	if (gsfs != NULL) {
150 		free(gsfs);
151 		gsfs = NULL;
152 	}
153 	allfs = getfsstat(NULL, 0, MNT_NOWAIT);
154 	if (allfs == -1)
155 		goto fail;
156 	gsfs = malloc(sizeof (gsfs[0]) * allfs * 2);
157 	if (gsfs == NULL)
158 		goto fail;
159 	allfs = getfsstat(gsfs, (long)(sizeof (gsfs[0]) * allfs * 2),
160 	    MNT_NOWAIT);
161 	if (allfs == -1)
162 		goto fail;
163 	sfs = realloc(gsfs, allfs * sizeof (gsfs[0]));
164 	if (sfs != NULL)
165 		gsfs = sfs;
166 	(void) pthread_rwlock_unlock(&gsfs_lock);
167 	return (0);
168 fail:
169 	error = errno;
170 	if (gsfs != NULL)
171 		free(gsfs);
172 	gsfs = NULL;
173 	allfs = 0;
174 	(void) pthread_rwlock_unlock(&gsfs_lock);
175 	return (error);
176 }
177 
178 int
179 getmntany(FILE *fd __unused, struct mnttab *mgetp, struct mnttab *mrefp)
180 {
181 	int i, error;
182 
183 	error = statfs_init();
184 	if (error != 0)
185 		return (error);
186 
187 	(void) pthread_rwlock_rdlock(&gsfs_lock);
188 
189 	for (i = 0; i < allfs; i++) {
190 		if (mrefp->mnt_special != NULL &&
191 		    strcmp(mrefp->mnt_special, gsfs[i].f_mntfromname) != 0) {
192 			continue;
193 		}
194 		if (mrefp->mnt_mountp != NULL &&
195 		    strcmp(mrefp->mnt_mountp, gsfs[i].f_mntonname) != 0) {
196 			continue;
197 		}
198 		if (mrefp->mnt_fstype != NULL &&
199 		    strcmp(mrefp->mnt_fstype, gsfs[i].f_fstypename) != 0) {
200 			continue;
201 		}
202 		statfs2mnttab(&gsfs[i], mgetp);
203 		(void) pthread_rwlock_unlock(&gsfs_lock);
204 		return (0);
205 	}
206 	(void) pthread_rwlock_unlock(&gsfs_lock);
207 	return (-1);
208 }
209 
210 int
211 getmntent(FILE *fp, struct mnttab *mp)
212 {
213 	int error, nfs;
214 
215 	nfs = (int)lseek(fileno(fp), 0, SEEK_CUR);
216 	if (nfs == -1)
217 		return (errno);
218 	/* If nfs is 0, we want to refresh out cache. */
219 	if (nfs == 0 || gsfs == NULL) {
220 		error = statfs_init();
221 		if (error != 0)
222 			return (error);
223 	}
224 	(void) pthread_rwlock_rdlock(&gsfs_lock);
225 	if (nfs >= allfs) {
226 		(void) pthread_rwlock_unlock(&gsfs_lock);
227 		return (-1);
228 	}
229 	statfs2mnttab(&gsfs[nfs], mp);
230 	(void) pthread_rwlock_unlock(&gsfs_lock);
231 	if (lseek(fileno(fp), 1, SEEK_CUR) == -1)
232 		return (errno);
233 	return (0);
234 }
235