Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/openzfs/lib/libshare/os/linux/nfs.c
48546 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
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
25
* Copyright (c) 2011 Gunnar Beutner
26
* Copyright (c) 2012 Cyril Plisko. All rights reserved.
27
* Copyright (c) 2019, 2022 by Delphix. All rights reserved.
28
*/
29
30
#include <dirent.h>
31
#include <stdio.h>
32
#include <string.h>
33
#include <errno.h>
34
#include <fcntl.h>
35
#include <sys/file.h>
36
#include <sys/stat.h>
37
#include <sys/types.h>
38
#include <sys/wait.h>
39
#include <unistd.h>
40
#include <libzfs.h>
41
#include <libshare.h>
42
#include "libshare_impl.h"
43
#include "nfs.h"
44
45
#define ZFS_EXPORTS_DIR "/etc/exports.d"
46
#define ZFS_EXPORTS_FILE ZFS_EXPORTS_DIR"/zfs.exports"
47
#define ZFS_EXPORTS_LOCK ZFS_EXPORTS_FILE".lock"
48
49
50
static boolean_t nfs_available(void);
51
static boolean_t exports_available(void);
52
53
typedef int (*nfs_shareopt_callback_t)(const char *opt, const char *value,
54
void *cookie);
55
56
typedef int (*nfs_host_callback_t)(FILE *tmpfile, const char *sharepath,
57
const char *host, const char *security, const char *access, void *cookie);
58
59
/*
60
* Invokes the specified callback function for each Solaris share option
61
* listed in the specified string.
62
*/
63
static int
64
foreach_nfs_shareopt(const char *shareopts,
65
nfs_shareopt_callback_t callback, void *cookie)
66
{
67
char *shareopts_dup, *opt, *cur, *value;
68
int was_nul, error;
69
70
if (shareopts == NULL)
71
return (SA_OK);
72
73
if (strcmp(shareopts, "on") == 0)
74
shareopts = "rw,crossmnt";
75
76
shareopts_dup = strdup(shareopts);
77
78
79
if (shareopts_dup == NULL)
80
return (SA_NO_MEMORY);
81
82
opt = shareopts_dup;
83
was_nul = 0;
84
85
while (1) {
86
cur = opt;
87
88
while (*cur != ',' && *cur != '\0')
89
cur++;
90
91
if (*cur == '\0')
92
was_nul = 1;
93
94
*cur = '\0';
95
96
if (cur > opt) {
97
value = strchr(opt, '=');
98
99
if (value != NULL) {
100
*value = '\0';
101
value++;
102
}
103
104
error = callback(opt, value, cookie);
105
106
if (error != SA_OK) {
107
free(shareopts_dup);
108
return (error);
109
}
110
}
111
112
opt = cur + 1;
113
114
if (was_nul)
115
break;
116
}
117
118
free(shareopts_dup);
119
120
return (SA_OK);
121
}
122
123
typedef struct nfs_host_cookie_s {
124
nfs_host_callback_t callback;
125
const char *sharepath;
126
void *cookie;
127
FILE *tmpfile;
128
const char *security;
129
} nfs_host_cookie_t;
130
131
/*
132
* Helper function for foreach_nfs_host. This function checks whether the
133
* current share option is a host specification and invokes a callback
134
* function with information about the host.
135
*/
136
static int
137
foreach_nfs_host_cb(const char *opt, const char *value, void *pcookie)
138
{
139
int error;
140
const char *access;
141
char *host_dup, *host, *next, *v6Literal;
142
nfs_host_cookie_t *udata = (nfs_host_cookie_t *)pcookie;
143
int cidr_len;
144
145
#ifdef DEBUG
146
fprintf(stderr, "foreach_nfs_host_cb: key=%s, value=%s\n", opt, value);
147
#endif
148
149
if (strcmp(opt, "sec") == 0)
150
udata->security = value;
151
152
if (strcmp(opt, "rw") == 0 || strcmp(opt, "ro") == 0) {
153
if (value == NULL)
154
value = "*";
155
156
access = opt;
157
158
host_dup = strdup(value);
159
160
if (host_dup == NULL)
161
return (SA_NO_MEMORY);
162
163
host = host_dup;
164
165
do {
166
if (*host == '[') {
167
host++;
168
v6Literal = strchr(host, ']');
169
if (v6Literal == NULL) {
170
free(host_dup);
171
return (SA_SYNTAX_ERR);
172
}
173
if (v6Literal[1] == '\0') {
174
*v6Literal = '\0';
175
next = NULL;
176
} else if (v6Literal[1] == '/') {
177
next = strchr(v6Literal + 2, ':');
178
if (next == NULL) {
179
cidr_len =
180
strlen(v6Literal + 1);
181
memmove(v6Literal,
182
v6Literal + 1,
183
cidr_len);
184
v6Literal[cidr_len] = '\0';
185
} else {
186
cidr_len = next - v6Literal - 1;
187
memmove(v6Literal,
188
v6Literal + 1,
189
cidr_len);
190
v6Literal[cidr_len] = '\0';
191
next++;
192
}
193
} else if (v6Literal[1] == ':') {
194
*v6Literal = '\0';
195
next = v6Literal + 2;
196
} else {
197
free(host_dup);
198
return (SA_SYNTAX_ERR);
199
}
200
} else {
201
next = strchr(host, ':');
202
if (next != NULL) {
203
*next = '\0';
204
next++;
205
}
206
}
207
208
error = udata->callback(udata->tmpfile,
209
udata->sharepath, host, udata->security,
210
access, udata->cookie);
211
212
if (error != SA_OK) {
213
free(host_dup);
214
215
return (error);
216
}
217
218
host = next;
219
} while (host != NULL);
220
221
free(host_dup);
222
}
223
224
return (SA_OK);
225
}
226
227
/*
228
* Invokes a callback function for all NFS hosts that are set for a share.
229
*/
230
static int
231
foreach_nfs_host(sa_share_impl_t impl_share, FILE *tmpfile,
232
nfs_host_callback_t callback, void *cookie)
233
{
234
nfs_host_cookie_t udata;
235
236
udata.callback = callback;
237
udata.sharepath = impl_share->sa_mountpoint;
238
udata.cookie = cookie;
239
udata.tmpfile = tmpfile;
240
udata.security = "sys";
241
242
return (foreach_nfs_shareopt(impl_share->sa_shareopts,
243
foreach_nfs_host_cb, &udata));
244
}
245
246
/*
247
* Converts a Solaris NFS host specification to its Linux equivalent.
248
*/
249
static const char *
250
get_linux_hostspec(const char *solaris_hostspec)
251
{
252
/*
253
* For now we just support CIDR masks (e.g. @192.168.0.0/16) and host
254
* wildcards (e.g. *.example.org).
255
*/
256
if (solaris_hostspec[0] == '@') {
257
/*
258
* Solaris host specifier, e.g. @192.168.0.0/16; we just need
259
* to skip the @ in this case
260
*/
261
return (solaris_hostspec + 1);
262
} else {
263
return (solaris_hostspec);
264
}
265
}
266
267
/*
268
* Adds a Linux share option to an array of NFS options.
269
*/
270
static int
271
add_linux_shareopt(char **plinux_opts, const char *key, const char *value)
272
{
273
size_t len = 0;
274
char *new_linux_opts;
275
276
if (*plinux_opts != NULL)
277
len = strlen(*plinux_opts);
278
279
new_linux_opts = realloc(*plinux_opts, len + 1 + strlen(key) +
280
(value ? 1 + strlen(value) : 0) + 1);
281
282
if (new_linux_opts == NULL)
283
return (SA_NO_MEMORY);
284
285
new_linux_opts[len] = '\0';
286
287
if (len > 0)
288
strcat(new_linux_opts, ",");
289
290
strcat(new_linux_opts, key);
291
292
if (value != NULL) {
293
strcat(new_linux_opts, "=");
294
strcat(new_linux_opts, value);
295
}
296
297
*plinux_opts = new_linux_opts;
298
299
return (SA_OK);
300
}
301
302
static int string_cmp(const void *lhs, const void *rhs) {
303
const char *const *l = lhs, *const *r = rhs;
304
return (strcmp(*l, *r));
305
}
306
307
/*
308
* Validates and converts a single Solaris share option to its Linux
309
* equivalent.
310
*/
311
static int
312
get_linux_shareopts_cb(const char *key, const char *value, void *cookie)
313
{
314
/* This list must remain sorted, since we bsearch() it */
315
static const char *const valid_keys[] = { "all_squash", "anongid",
316
"anonuid", "async", "auth_nlm", "crossmnt", "fsid", "fsuid", "hide",
317
"insecure", "insecure_locks", "mountpoint", "mp", "no_acl",
318
"no_all_squash", "no_auth_nlm", "no_root_squash",
319
"no_subtree_check", "no_wdelay", "nohide", "refer", "replicas",
320
"root_squash", "secure", "secure_locks", "subtree_check", "sync",
321
"wdelay" };
322
323
char **plinux_opts = (char **)cookie;
324
char *host, *val_dup, *literal, *next;
325
326
if (strcmp(key, "sec") == 0)
327
return (SA_OK);
328
329
if (strcmp(key, "ro") == 0 || strcmp(key, "rw") == 0) {
330
if (value == NULL || strlen(value) == 0)
331
return (SA_OK);
332
val_dup = strdup(value);
333
host = val_dup;
334
if (host == NULL)
335
return (SA_NO_MEMORY);
336
do {
337
if (*host == '[') {
338
host++;
339
literal = strchr(host, ']');
340
if (literal == NULL) {
341
free(val_dup);
342
return (SA_SYNTAX_ERR);
343
}
344
if (literal[1] == '\0')
345
next = NULL;
346
else if (literal[1] == '/') {
347
next = strchr(literal + 2, ':');
348
if (next != NULL)
349
++next;
350
} else if (literal[1] == ':')
351
next = literal + 2;
352
else {
353
free(val_dup);
354
return (SA_SYNTAX_ERR);
355
}
356
} else {
357
next = strchr(host, ':');
358
if (next != NULL)
359
++next;
360
}
361
host = next;
362
} while (host != NULL);
363
free(val_dup);
364
return (SA_OK);
365
}
366
367
if (strcmp(key, "anon") == 0)
368
key = "anonuid";
369
370
if (strcmp(key, "root_mapping") == 0) {
371
(void) add_linux_shareopt(plinux_opts, "root_squash", NULL);
372
key = "anonuid";
373
}
374
375
if (strcmp(key, "nosub") == 0)
376
key = "subtree_check";
377
378
if (bsearch(&key, valid_keys, ARRAY_SIZE(valid_keys),
379
sizeof (*valid_keys), string_cmp) == NULL)
380
return (SA_SYNTAX_ERR);
381
382
(void) add_linux_shareopt(plinux_opts, key, value);
383
384
return (SA_OK);
385
}
386
387
/*
388
* Takes a string containing Solaris share options (e.g. "sync,no_acl") and
389
* converts them to a NULL-terminated array of Linux NFS options.
390
*/
391
static int
392
get_linux_shareopts(const char *shareopts, char **plinux_opts)
393
{
394
int error;
395
396
assert(plinux_opts != NULL);
397
398
*plinux_opts = NULL;
399
400
/* no_subtree_check - Default as of nfs-utils v1.1.0 */
401
(void) add_linux_shareopt(plinux_opts, "no_subtree_check", NULL);
402
403
/* mountpoint - Restrict exports to ZFS mountpoints */
404
(void) add_linux_shareopt(plinux_opts, "mountpoint", NULL);
405
406
error = foreach_nfs_shareopt(shareopts, get_linux_shareopts_cb,
407
plinux_opts);
408
409
if (error != SA_OK) {
410
free(*plinux_opts);
411
*plinux_opts = NULL;
412
}
413
414
return (error);
415
}
416
417
/*
418
* This function populates an entry into /etc/exports.d/zfs.exports.
419
* This file is consumed by the linux nfs server so that zfs shares are
420
* automatically exported upon boot or whenever the nfs server restarts.
421
*/
422
static int
423
nfs_add_entry(FILE *tmpfile, const char *sharepath,
424
const char *host, const char *security, const char *access_opts,
425
void *pcookie)
426
{
427
const char *linux_opts = (const char *)pcookie;
428
429
if (linux_opts == NULL)
430
linux_opts = "";
431
432
boolean_t need_free;
433
char *mp;
434
int rc = nfs_escape_mountpoint(sharepath, &mp, &need_free);
435
if (rc != SA_OK)
436
return (rc);
437
if (fprintf(tmpfile, "%s %s(sec=%s,%s,%s)\n", mp,
438
get_linux_hostspec(host), security, access_opts,
439
linux_opts) < 0) {
440
fprintf(stderr, "failed to write to temporary file\n");
441
rc = SA_SYSTEM_ERR;
442
}
443
444
if (need_free)
445
free(mp);
446
return (rc);
447
}
448
449
/*
450
* Enables NFS sharing for the specified share.
451
*/
452
static int
453
nfs_enable_share_impl(sa_share_impl_t impl_share, FILE *tmpfile)
454
{
455
char *linux_opts = NULL;
456
int error = get_linux_shareopts(impl_share->sa_shareopts, &linux_opts);
457
if (error != SA_OK)
458
return (error);
459
460
error = foreach_nfs_host(impl_share, tmpfile, nfs_add_entry,
461
linux_opts);
462
free(linux_opts);
463
return (error);
464
}
465
466
static int
467
nfs_enable_share(sa_share_impl_t impl_share)
468
{
469
if (!nfs_available())
470
return (SA_SYSTEM_ERR);
471
472
return (nfs_toggle_share(
473
ZFS_EXPORTS_LOCK, ZFS_EXPORTS_FILE, ZFS_EXPORTS_DIR, impl_share,
474
nfs_enable_share_impl));
475
}
476
477
/*
478
* Disables NFS sharing for the specified share.
479
*/
480
static int
481
nfs_disable_share_impl(sa_share_impl_t impl_share, FILE *tmpfile)
482
{
483
(void) impl_share, (void) tmpfile;
484
return (SA_OK);
485
}
486
487
static int
488
nfs_disable_share(sa_share_impl_t impl_share)
489
{
490
if (!nfs_available())
491
return (SA_OK);
492
493
return (nfs_toggle_share(
494
ZFS_EXPORTS_LOCK, ZFS_EXPORTS_FILE, ZFS_EXPORTS_DIR, impl_share,
495
nfs_disable_share_impl));
496
}
497
498
static boolean_t
499
nfs_is_shared(sa_share_impl_t impl_share)
500
{
501
if (!nfs_available())
502
return (SA_SYSTEM_ERR);
503
504
return (nfs_is_shared_impl(ZFS_EXPORTS_FILE, impl_share));
505
}
506
507
/*
508
* Checks whether the specified NFS share options are syntactically correct.
509
*/
510
static int
511
nfs_validate_shareopts(const char *shareopts)
512
{
513
char *linux_opts = NULL;
514
515
if (strlen(shareopts) == 0)
516
return (SA_SYNTAX_ERR);
517
518
int error = get_linux_shareopts(shareopts, &linux_opts);
519
if (error != SA_OK)
520
return (error);
521
522
free(linux_opts);
523
return (SA_OK);
524
}
525
526
static int
527
nfs_commit_shares(void)
528
{
529
if (!nfs_available())
530
return (SA_SYSTEM_ERR);
531
532
char *argv[] = {
533
(char *)"/usr/sbin/exportfs",
534
(char *)"-ra",
535
NULL
536
};
537
538
return (libzfs_run_process(argv[0], argv, 0));
539
}
540
541
static void
542
nfs_truncate_shares(void)
543
{
544
if (!exports_available())
545
return;
546
nfs_reset_shares(ZFS_EXPORTS_LOCK, ZFS_EXPORTS_FILE);
547
}
548
549
const sa_fstype_t libshare_nfs_type = {
550
.enable_share = nfs_enable_share,
551
.disable_share = nfs_disable_share,
552
.is_shared = nfs_is_shared,
553
554
.validate_shareopts = nfs_validate_shareopts,
555
.commit_shares = nfs_commit_shares,
556
.truncate_shares = nfs_truncate_shares,
557
};
558
559
static boolean_t
560
nfs_available(void)
561
{
562
static int avail;
563
564
if (!avail) {
565
if (access("/usr/sbin/exportfs", F_OK) != 0)
566
avail = -1;
567
else
568
avail = 1;
569
}
570
571
return (avail == 1);
572
}
573
574
static boolean_t
575
exports_available(void)
576
{
577
static int avail;
578
579
if (!avail) {
580
if (access(ZFS_EXPORTS_DIR, F_OK) != 0)
581
avail = -1;
582
else
583
avail = 1;
584
}
585
586
return (avail == 1);
587
}
588
589