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