Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/openzfs/lib/libshare/nfs.c
48378 views
1
// SPDX-License-Identifier: CDDL-1.0
2
/*
3
* CDDL HEADER START
4
*
5
* The contents of this file are subject to the terms of the
6
* Common Development and Distribution License (the "License").
7
* You may not use this file except in compliance with the License.
8
*
9
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10
* or https://opensource.org/licenses/CDDL-1.0.
11
* See the License for the specific language governing permissions
12
* and limitations under the License.
13
*
14
* When distributing Covered Code, include this CDDL HEADER in each
15
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16
* If applicable, add the following below this CDDL HEADER, with the
17
* fields enclosed by brackets "[]" replaced with your own identifying
18
* information: Portions Copyright [yyyy] [name of copyright owner]
19
*
20
* CDDL HEADER END
21
*/
22
23
24
#include <sys/types.h>
25
#include <sys/stat.h>
26
#include <sys/file.h>
27
#include <fcntl.h>
28
#include <ctype.h>
29
#include <stdio.h>
30
#include <errno.h>
31
#include <libshare.h>
32
#include <unistd.h>
33
#include <libzutil.h>
34
#include "nfs.h"
35
36
37
/*
38
* nfs_exports_[lock|unlock] are used to guard against conconcurrent
39
* updates to the exports file. Each protocol is responsible for
40
* providing the necessary locking to ensure consistency.
41
*/
42
static int
43
nfs_exports_lock(const char *name, int *nfs_lock_fd)
44
{
45
int err;
46
47
*nfs_lock_fd = open(name, O_RDWR | O_CREAT | O_CLOEXEC, 0600);
48
if (*nfs_lock_fd == -1) {
49
err = errno;
50
fprintf(stderr, "failed to lock %s: %s\n", name,
51
zfs_strerror(err));
52
return (err);
53
}
54
55
while ((err = flock(*nfs_lock_fd, LOCK_EX)) != 0 && errno == EINTR)
56
;
57
if (err != 0) {
58
err = errno;
59
fprintf(stderr, "failed to lock %s: %s\n", name,
60
zfs_strerror(err));
61
(void) close(*nfs_lock_fd);
62
*nfs_lock_fd = -1;
63
return (err);
64
}
65
66
return (0);
67
}
68
69
static void
70
nfs_exports_unlock(const char *name, int *nfs_lock_fd)
71
{
72
verify(*nfs_lock_fd > 0);
73
74
if (flock(*nfs_lock_fd, LOCK_UN) != 0)
75
fprintf(stderr, "failed to unlock %s: %s\n",
76
name, zfs_strerror(errno));
77
78
(void) close(*nfs_lock_fd);
79
*nfs_lock_fd = -1;
80
}
81
82
struct tmpfile {
83
/*
84
* This only needs to be as wide as ZFS_EXPORTS_FILE and mktemp suffix,
85
* 64 is more than enough.
86
*/
87
char name[64];
88
FILE *fp;
89
};
90
91
static boolean_t
92
nfs_init_tmpfile(const char *prefix, const char *mdir, struct tmpfile *tmpf)
93
{
94
if (mdir != NULL &&
95
mkdir(mdir, 0755) < 0 &&
96
errno != EEXIST) {
97
fprintf(stderr, "failed to create %s: %s\n",
98
// cppcheck-suppress uninitvar
99
mdir, zfs_strerror(errno));
100
return (B_FALSE);
101
}
102
103
strlcpy(tmpf->name, prefix, sizeof (tmpf->name));
104
strlcat(tmpf->name, ".XXXXXXXX", sizeof (tmpf->name));
105
106
int fd = mkostemp(tmpf->name, O_CLOEXEC);
107
if (fd == -1) {
108
fprintf(stderr, "Unable to create temporary file: %s",
109
zfs_strerror(errno));
110
return (B_FALSE);
111
}
112
113
tmpf->fp = fdopen(fd, "w+");
114
if (tmpf->fp == NULL) {
115
fprintf(stderr, "Unable to reopen temporary file: %s",
116
zfs_strerror(errno));
117
close(fd);
118
return (B_FALSE);
119
}
120
121
return (B_TRUE);
122
}
123
124
static void
125
nfs_abort_tmpfile(struct tmpfile *tmpf)
126
{
127
unlink(tmpf->name);
128
fclose(tmpf->fp);
129
}
130
131
static int
132
nfs_fini_tmpfile(const char *exports, struct tmpfile *tmpf)
133
{
134
if (fflush(tmpf->fp) != 0) {
135
fprintf(stderr, "Failed to write to temporary file: %s\n",
136
zfs_strerror(errno));
137
nfs_abort_tmpfile(tmpf);
138
return (SA_SYSTEM_ERR);
139
}
140
141
if (rename(tmpf->name, exports) == -1) {
142
fprintf(stderr, "Unable to rename %s -> %s: %s\n",
143
tmpf->name, exports, zfs_strerror(errno));
144
nfs_abort_tmpfile(tmpf);
145
return (SA_SYSTEM_ERR);
146
}
147
148
(void) fchmod(fileno(tmpf->fp), 0644);
149
fclose(tmpf->fp);
150
return (SA_OK);
151
}
152
153
int
154
nfs_escape_mountpoint(const char *mp, char **out, boolean_t *need_free)
155
{
156
if (strpbrk(mp, "\t\n\v\f\r \\") == NULL) {
157
*out = (char *)mp;
158
*need_free = B_FALSE;
159
return (SA_OK);
160
} else {
161
size_t len = strlen(mp);
162
*out = malloc(len * 4 + 1);
163
if (!*out)
164
return (SA_NO_MEMORY);
165
*need_free = B_TRUE;
166
167
char *oc = *out;
168
for (const char *c = mp; c < mp + len; ++c)
169
if (memchr("\t\n\v\f\r \\", *c,
170
strlen("\t\n\v\f\r \\"))) {
171
sprintf(oc, "\\%03hho", *c);
172
oc += 4;
173
} else
174
*oc++ = *c;
175
*oc = '\0';
176
}
177
178
return (SA_OK);
179
}
180
181
static int
182
nfs_process_exports(const char *exports, const char *mountpoint,
183
boolean_t (*cbk)(void *userdata, char *line, boolean_t found_mountpoint),
184
void *userdata)
185
{
186
int error = SA_OK;
187
boolean_t cont = B_TRUE;
188
189
FILE *oldfp = fopen(exports, "re");
190
if (oldfp != NULL) {
191
boolean_t need_mp_free;
192
char *mp;
193
if ((error = nfs_escape_mountpoint(mountpoint,
194
&mp, &need_mp_free)) != SA_OK) {
195
(void) fclose(oldfp);
196
return (error);
197
}
198
199
char *buf = NULL, *sep;
200
size_t buflen = 0, mplen = strlen(mp);
201
202
while (cont && getline(&buf, &buflen, oldfp) != -1) {
203
if (buf[0] == '\n' || buf[0] == '#')
204
continue;
205
206
cont = cbk(userdata, buf,
207
(sep = strpbrk(buf, "\t \n")) != NULL &&
208
sep - buf == mplen &&
209
strncmp(buf, mp, mplen) == 0);
210
}
211
free(buf);
212
if (need_mp_free)
213
free(mp);
214
215
if (ferror(oldfp) != 0)
216
error = ferror(oldfp);
217
218
if (fclose(oldfp) != 0) {
219
fprintf(stderr, "Unable to close file %s: %s\n",
220
exports, zfs_strerror(errno));
221
error = error != SA_OK ? error : SA_SYSTEM_ERR;
222
}
223
}
224
225
return (error);
226
}
227
228
static boolean_t
229
nfs_copy_entries_cb(void *userdata, char *line, boolean_t found_mountpoint)
230
{
231
FILE *newfp = userdata;
232
if (!found_mountpoint)
233
fputs(line, newfp);
234
return (B_TRUE);
235
}
236
237
/*
238
* Copy all entries from the exports file (if it exists) to newfp,
239
* omitting any entries for the specified mountpoint.
240
*/
241
static int
242
nfs_copy_entries(FILE *newfp, const char *exports, const char *mountpoint)
243
{
244
fputs(FILE_HEADER, newfp);
245
246
int error = nfs_process_exports(
247
exports, mountpoint, nfs_copy_entries_cb, newfp);
248
249
if (error == SA_OK && ferror(newfp) != 0)
250
error = ferror(newfp);
251
252
return (error);
253
}
254
255
int
256
nfs_toggle_share(const char *lockfile, const char *exports,
257
const char *expdir, sa_share_impl_t impl_share,
258
int(*cbk)(sa_share_impl_t impl_share, FILE *tmpfile))
259
{
260
int error, nfs_lock_fd = -1;
261
struct tmpfile tmpf;
262
263
if (!nfs_init_tmpfile(exports, expdir, &tmpf))
264
return (SA_SYSTEM_ERR);
265
266
error = nfs_exports_lock(lockfile, &nfs_lock_fd);
267
if (error != 0) {
268
nfs_abort_tmpfile(&tmpf);
269
return (error);
270
}
271
272
error = nfs_copy_entries(tmpf.fp, exports, impl_share->sa_mountpoint);
273
if (error != SA_OK)
274
goto fullerr;
275
276
error = cbk(impl_share, tmpf.fp);
277
if (error != SA_OK)
278
goto fullerr;
279
280
error = nfs_fini_tmpfile(exports, &tmpf);
281
nfs_exports_unlock(lockfile, &nfs_lock_fd);
282
return (error);
283
284
fullerr:
285
nfs_abort_tmpfile(&tmpf);
286
nfs_exports_unlock(lockfile, &nfs_lock_fd);
287
return (error);
288
}
289
290
void
291
nfs_reset_shares(const char *lockfile, const char *exports)
292
{
293
int nfs_lock_fd = -1;
294
295
if (nfs_exports_lock(lockfile, &nfs_lock_fd) == 0) {
296
(void) ! truncate(exports, 0);
297
nfs_exports_unlock(lockfile, &nfs_lock_fd);
298
}
299
}
300
301
static boolean_t
302
nfs_is_shared_cb(void *userdata, char *line, boolean_t found_mountpoint)
303
{
304
(void) line;
305
306
boolean_t *found = userdata;
307
*found = found_mountpoint;
308
return (!found_mountpoint);
309
}
310
311
boolean_t
312
nfs_is_shared_impl(const char *exports, sa_share_impl_t impl_share)
313
{
314
boolean_t found = B_FALSE;
315
nfs_process_exports(exports, impl_share->sa_mountpoint,
316
nfs_is_shared_cb, &found);
317
return (found);
318
}
319
320