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
44static char *
45mntopt(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
66char *
67hasmntopt(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
83static void
84optadd(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
92static __thread char gfstypename[MFSNAMELEN];
93static __thread char gmntfromname[MNAMELEN];
94static __thread char gmntonname[MNAMELEN];
95static __thread char gmntopts[MNTMAXSTR];
96
97void
98statfs2mnttab(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
137static pthread_rwlock_t gsfs_lock = PTHREAD_RWLOCK_INITIALIZER;
138static struct statfs *gsfs = NULL;
139static int allfs = 0;
140
141static int
142statfs_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);
168fail:
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
178int
179getmntany(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
210int
211getmntent(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