Lines 49-54
__FBSDID("$FreeBSD: head/usr.sbin/mountd
Link Here
|
49 |
|
49 |
|
50 |
#include <sys/param.h> |
50 |
#include <sys/param.h> |
51 |
#include <sys/fcntl.h> |
51 |
#include <sys/fcntl.h> |
|
|
52 |
#include <sys/fnv_hash.h> |
52 |
#include <sys/linker.h> |
53 |
#include <sys/linker.h> |
53 |
#include <sys/module.h> |
54 |
#include <sys/module.h> |
54 |
#include <sys/mount.h> |
55 |
#include <sys/mount.h> |
Lines 56-61
__FBSDID("$FreeBSD: head/usr.sbin/mountd
Link Here
|
56 |
#include <sys/stat.h> |
57 |
#include <sys/stat.h> |
57 |
#include <sys/sysctl.h> |
58 |
#include <sys/sysctl.h> |
58 |
#include <sys/syslog.h> |
59 |
#include <sys/syslog.h> |
|
|
60 |
#include <sys/sdt.h> |
59 |
|
61 |
|
60 |
#include <rpc/rpc.h> |
62 |
#include <rpc/rpc.h> |
61 |
#include <rpc/rpc_com.h> |
63 |
#include <rpc/rpc_com.h> |
Lines 85-90
__FBSDID("$FreeBSD: head/usr.sbin/mountd
Link Here
|
85 |
#include <unistd.h> |
87 |
#include <unistd.h> |
86 |
#include "pathnames.h" |
88 |
#include "pathnames.h" |
87 |
#include "mntopts.h" |
89 |
#include "mntopts.h" |
|
|
90 |
#include "mountd_dt.h" |
88 |
|
91 |
|
89 |
#ifdef DEBUG |
92 |
#ifdef DEBUG |
90 |
#include <stdarg.h> |
93 |
#include <stdarg.h> |
Lines 114-123
struct dirlist {
Link Here
|
114 |
struct exportlist { |
117 |
struct exportlist { |
115 |
struct dirlist *ex_dirl; |
118 |
struct dirlist *ex_dirl; |
116 |
struct dirlist *ex_defdir; |
119 |
struct dirlist *ex_defdir; |
|
|
120 |
struct grouplist *ex_grphead; |
117 |
int ex_flag; |
121 |
int ex_flag; |
118 |
fsid_t ex_fs; |
122 |
fsid_t ex_fs; |
119 |
char *ex_fsdir; |
123 |
char *ex_fsdir; |
120 |
char *ex_indexfile; |
124 |
char *ex_indexfile; |
|
|
125 |
struct xucred ex_defanon; |
126 |
int ex_defexflags; |
121 |
int ex_numsecflavors; |
127 |
int ex_numsecflavors; |
122 |
int ex_secflavors[MAXSECFLAVORS]; |
128 |
int ex_secflavors[MAXSECFLAVORS]; |
123 |
int ex_defnumsecflavors; |
129 |
int ex_defnumsecflavors; |
Lines 127-132
struct exportlist {
Link Here
|
127 |
}; |
133 |
}; |
128 |
/* ex_flag bits */ |
134 |
/* ex_flag bits */ |
129 |
#define EX_LINKED 0x1 |
135 |
#define EX_LINKED 0x1 |
|
|
136 |
#define EX_DONE 0x2 |
137 |
#define EX_DEFSET 0x4 |
138 |
#define EX_PUBLICFH 0x8 |
139 |
|
140 |
SLIST_HEAD(exportlisthead, exportlist); |
130 |
|
141 |
|
131 |
struct netmsk { |
142 |
struct netmsk { |
132 |
struct sockaddr_storage nt_net; |
143 |
struct sockaddr_storage nt_net; |
Lines 143-148
struct grouplist {
Link Here
|
143 |
int gr_type; |
154 |
int gr_type; |
144 |
union grouptypes gr_ptr; |
155 |
union grouptypes gr_ptr; |
145 |
struct grouplist *gr_next; |
156 |
struct grouplist *gr_next; |
|
|
157 |
struct xucred gr_anon; |
158 |
int gr_exflags; |
159 |
int gr_flag; |
146 |
int gr_numsecflavors; |
160 |
int gr_numsecflavors; |
147 |
int gr_secflavors[MAXSECFLAVORS]; |
161 |
int gr_secflavors[MAXSECFLAVORS]; |
148 |
}; |
162 |
}; |
Lines 153-158
struct grouplist {
Link Here
|
153 |
#define GT_DEFAULT 0x3 |
167 |
#define GT_DEFAULT 0x3 |
154 |
#define GT_IGNORE 0x5 |
168 |
#define GT_IGNORE 0x5 |
155 |
|
169 |
|
|
|
170 |
/* Group flags */ |
171 |
#define GR_FND 0x1 |
172 |
|
156 |
struct hostlist { |
173 |
struct hostlist { |
157 |
int ht_flag; /* Uses DP_xx bits */ |
174 |
int ht_flag; /* Uses DP_xx bits */ |
158 |
struct grouplist *ht_grp; |
175 |
struct grouplist *ht_grp; |
Lines 172-178
struct fhreturn {
Link Here
|
172 |
/* Global defs */ |
189 |
/* Global defs */ |
173 |
static char *add_expdir(struct dirlist **, char *, int); |
190 |
static char *add_expdir(struct dirlist **, char *, int); |
174 |
static void add_dlist(struct dirlist **, struct dirlist *, |
191 |
static void add_dlist(struct dirlist **, struct dirlist *, |
175 |
struct grouplist *, int, struct exportlist *); |
192 |
struct grouplist *, int, struct exportlist *, |
|
|
193 |
struct xucred *, int); |
176 |
static void add_mlist(char *, char *); |
194 |
static void add_mlist(char *, char *); |
177 |
static int check_dirpath(char *); |
195 |
static int check_dirpath(char *); |
178 |
static int check_options(struct dirlist *); |
196 |
static int check_options(struct dirlist *); |
Lines 185-201
static void complete_service(struct netc
Link Here
|
185 |
static void clearout_service(void); |
203 |
static void clearout_service(void); |
186 |
static void del_mlist(char *hostp, char *dirp); |
204 |
static void del_mlist(char *hostp, char *dirp); |
187 |
static struct dirlist *dirp_search(struct dirlist *, char *); |
205 |
static struct dirlist *dirp_search(struct dirlist *, char *); |
|
|
206 |
static int do_export_mount(struct exportlist *, struct statfs *); |
188 |
static int do_mount(struct exportlist *, struct grouplist *, int, |
207 |
static int do_mount(struct exportlist *, struct grouplist *, int, |
189 |
struct xucred *, char *, int, struct statfs *); |
208 |
struct xucred *, char *, int, struct statfs *, int, int *); |
190 |
static int do_opt(char **, char **, struct exportlist *, |
209 |
static int do_opt(char **, char **, struct exportlist *, |
191 |
struct grouplist *, int *, int *, struct xucred *); |
210 |
struct grouplist *, int *, int *, struct xucred *); |
192 |
static struct exportlist *ex_search(fsid_t *); |
211 |
static struct exportlist *ex_search(fsid_t *, struct exportlisthead *); |
193 |
static struct exportlist *get_exp(void); |
212 |
static struct exportlist *get_exp(void); |
194 |
static void free_dir(struct dirlist *); |
213 |
static void free_dir(struct dirlist *); |
195 |
static void free_exp(struct exportlist *); |
214 |
static void free_exp(struct exportlist *); |
196 |
static void free_grp(struct grouplist *); |
215 |
static void free_grp(struct grouplist *); |
197 |
static void free_host(struct hostlist *); |
216 |
static void free_host(struct hostlist *); |
198 |
static void get_exportlist(void); |
217 |
static void free_v4rootexp(void); |
|
|
218 |
static void get_exportlist_one(int); |
219 |
static void get_exportlist(int); |
220 |
static void insert_exports(struct exportlist *, struct exportlisthead *); |
221 |
static void free_exports(struct exportlisthead *); |
222 |
static void read_exportfile(int); |
223 |
static int compare_nmount_exportlist(struct iovec *, int, char *); |
224 |
static int compare_export(struct exportlist *, struct exportlist *); |
225 |
static int compare_cred(struct xucred *, struct xucred *); |
226 |
static int compare_secflavor(int *, int *, int); |
227 |
static void delete_export(struct iovec *, int, struct statfs *, char *); |
199 |
static int get_host(char *, struct grouplist *, struct grouplist *); |
228 |
static int get_host(char *, struct grouplist *, struct grouplist *); |
200 |
static struct hostlist *get_ht(void); |
229 |
static struct hostlist *get_ht(void); |
201 |
static int get_line(void); |
230 |
static int get_line(void); |
Lines 204-210
static int get_net(char *, struct netmsk
Link Here
|
204 |
static void getexp_err(struct exportlist *, struct grouplist *, const char *); |
233 |
static void getexp_err(struct exportlist *, struct grouplist *, const char *); |
205 |
static struct grouplist *get_grp(void); |
234 |
static struct grouplist *get_grp(void); |
206 |
static void hang_dirp(struct dirlist *, struct grouplist *, |
235 |
static void hang_dirp(struct dirlist *, struct grouplist *, |
207 |
struct exportlist *, int); |
236 |
struct exportlist *, int, struct xucred *, int); |
208 |
static void huphandler(int sig); |
237 |
static void huphandler(int sig); |
209 |
static int makemask(struct sockaddr_storage *ssp, int bitlen); |
238 |
static int makemask(struct sockaddr_storage *ssp, int bitlen); |
210 |
static void mntsrv(struct svc_req *, SVCXPRT *); |
239 |
static void mntsrv(struct svc_req *, SVCXPRT *); |
Lines 227-235
static int xdr_fhs(XDR *, caddr_t);
Link Here
|
227 |
static int xdr_mlist(XDR *, caddr_t); |
256 |
static int xdr_mlist(XDR *, caddr_t); |
228 |
static void terminate(int); |
257 |
static void terminate(int); |
229 |
|
258 |
|
230 |
static SLIST_HEAD(, exportlist) exphead = SLIST_HEAD_INITIALIZER(exphead); |
259 |
#define EXPHASH(f) (fnv_32_buf((f), sizeof(fsid_t), 0) % exphashsize) |
231 |
static SLIST_HEAD(, mountlist) mlhead = SLIST_HEAD_INITIALIZER(mlhead); |
260 |
static struct exportlisthead *exphead = NULL; |
232 |
static struct grouplist *grphead; |
261 |
static struct exportlisthead *oldexphead = NULL; |
|
|
262 |
static int exphashsize = 0; |
263 |
static SLIST_HEAD(, mountlist) mlhead = SLIST_HEAD_INITIALIZER(&mlhead); |
233 |
static char *exnames_default[2] = { _PATH_EXPORTS, NULL }; |
264 |
static char *exnames_default[2] = { _PATH_EXPORTS, NULL }; |
234 |
static char **exnames; |
265 |
static char **exnames; |
235 |
static char **hosts = NULL; |
266 |
static char **hosts = NULL; |
Lines 260-266
static int have_v6 = 1;
Link Here
|
260 |
|
291 |
|
261 |
static int v4root_phase = 0; |
292 |
static int v4root_phase = 0; |
262 |
static char v4root_dirpath[PATH_MAX + 1]; |
293 |
static char v4root_dirpath[PATH_MAX + 1]; |
|
|
294 |
static struct exportlist *v4root_ep = NULL; |
263 |
static int has_publicfh = 0; |
295 |
static int has_publicfh = 0; |
|
|
296 |
static int has_set_publicfh = 0; |
264 |
|
297 |
|
265 |
static struct pidfh *pfh = NULL; |
298 |
static struct pidfh *pfh = NULL; |
266 |
/* Bits for opt_flags above */ |
299 |
/* Bits for opt_flags above */ |
Lines 368-376
main(int argc, char **argv)
Link Here
|
368 |
in_port_t svcport; |
401 |
in_port_t svcport; |
369 |
int c, k, s; |
402 |
int c, k, s; |
370 |
int maxrec = RPC_MAXDATASIZE; |
403 |
int maxrec = RPC_MAXDATASIZE; |
371 |
int attempt_cnt, port_len, port_pos, ret; |
404 |
int attempt_cnt, passno, port_len, port_pos, ret; |
372 |
char **port_list; |
405 |
char **port_list; |
373 |
|
406 |
|
|
|
407 |
passno = 0; |
374 |
/* Check that another mountd isn't already running. */ |
408 |
/* Check that another mountd isn't already running. */ |
375 |
pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &otherpid); |
409 |
pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &otherpid); |
376 |
if (pfh == NULL) { |
410 |
if (pfh == NULL) { |
Lines 385-391
main(int argc, char **argv)
Link Here
|
385 |
else |
419 |
else |
386 |
close(s); |
420 |
close(s); |
387 |
|
421 |
|
388 |
while ((c = getopt(argc, argv, "2deh:lnp:rS")) != -1) |
422 |
while ((c = getopt(argc, argv, "2deh:Ilnp:rS")) != -1) |
389 |
switch (c) { |
423 |
switch (c) { |
390 |
case '2': |
424 |
case '2': |
391 |
force_v2 = 1; |
425 |
force_v2 = 1; |
Lines 437-442
main(int argc, char **argv)
Link Here
|
437 |
case 'S': |
471 |
case 'S': |
438 |
suspend_nfsd = 1; |
472 |
suspend_nfsd = 1; |
439 |
break; |
473 |
break; |
|
|
474 |
case 'I': |
475 |
passno = 1; |
476 |
break; |
440 |
default: |
477 |
default: |
441 |
usage(); |
478 |
usage(); |
442 |
} |
479 |
} |
Lines 449-455
main(int argc, char **argv)
Link Here
|
449 |
|
486 |
|
450 |
argc -= optind; |
487 |
argc -= optind; |
451 |
argv += optind; |
488 |
argv += optind; |
452 |
grphead = (struct grouplist *)NULL; |
|
|
453 |
if (argc > 0) |
489 |
if (argc > 0) |
454 |
exnames = argv; |
490 |
exnames = argv; |
455 |
else |
491 |
else |
Lines 457-463
main(int argc, char **argv)
Link Here
|
457 |
openlog("mountd", LOG_PID, LOG_DAEMON); |
493 |
openlog("mountd", LOG_PID, LOG_DAEMON); |
458 |
if (debug) |
494 |
if (debug) |
459 |
warnx("getting export list"); |
495 |
warnx("getting export list"); |
460 |
get_exportlist(); |
496 |
get_exportlist(0); |
461 |
if (debug) |
497 |
if (debug) |
462 |
warnx("getting mount list"); |
498 |
warnx("getting mount list"); |
463 |
get_mountlist(); |
499 |
get_mountlist(); |
Lines 628-634
main(int argc, char **argv)
Link Here
|
628 |
/* Expand svc_run() here so that we can call get_exportlist(). */ |
664 |
/* Expand svc_run() here so that we can call get_exportlist(). */ |
629 |
for (;;) { |
665 |
for (;;) { |
630 |
if (got_sighup) { |
666 |
if (got_sighup) { |
631 |
get_exportlist(); |
667 |
get_exportlist(passno); |
632 |
got_sighup = 0; |
668 |
got_sighup = 0; |
633 |
} |
669 |
} |
634 |
readfds = svc_fdset; |
670 |
readfds = svc_fdset; |
Lines 1087-1093
mntsrv(struct svc_req *rqstp, SVCXPRT *t
Link Here
|
1087 |
if (bad) |
1123 |
if (bad) |
1088 |
ep = NULL; |
1124 |
ep = NULL; |
1089 |
else |
1125 |
else |
1090 |
ep = ex_search(&fsb.f_fsid); |
1126 |
ep = ex_search(&fsb.f_fsid, exphead); |
1091 |
hostset = defset = 0; |
1127 |
hostset = defset = 0; |
1092 |
if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset, |
1128 |
if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset, |
1093 |
&numsecflavors, &secflavorsp) || |
1129 |
&numsecflavors, &secflavorsp) || |
Lines 1302-1322
xdr_explist_common(XDR *xdrsp, caddr_t c
Link Here
|
1302 |
int false = 0; |
1338 |
int false = 0; |
1303 |
int putdef; |
1339 |
int putdef; |
1304 |
sigset_t sighup_mask; |
1340 |
sigset_t sighup_mask; |
|
|
1341 |
int i; |
1305 |
|
1342 |
|
1306 |
sigemptyset(&sighup_mask); |
1343 |
sigemptyset(&sighup_mask); |
1307 |
sigaddset(&sighup_mask, SIGHUP); |
1344 |
sigaddset(&sighup_mask, SIGHUP); |
1308 |
sigprocmask(SIG_BLOCK, &sighup_mask, NULL); |
1345 |
sigprocmask(SIG_BLOCK, &sighup_mask, NULL); |
1309 |
|
1346 |
|
1310 |
SLIST_FOREACH(ep, &exphead, entries) { |
1347 |
for (i = 0; i < exphashsize; i++) |
1311 |
putdef = 0; |
1348 |
SLIST_FOREACH(ep, &exphead[i], entries) { |
1312 |
if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, |
1349 |
putdef = 0; |
1313 |
&putdef, brief)) |
1350 |
if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, |
1314 |
goto errout; |
1351 |
&putdef, brief)) |
1315 |
if (ep->ex_defdir && putdef == 0 && |
1352 |
goto errout; |
1316 |
put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL, |
1353 |
if (ep->ex_defdir && putdef == 0 && |
1317 |
&putdef, brief)) |
1354 |
put_exlist(ep->ex_defdir, xdrsp, NULL, |
1318 |
goto errout; |
1355 |
&putdef, brief)) |
1319 |
} |
1356 |
goto errout; |
|
|
1357 |
} |
1320 |
sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); |
1358 |
sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); |
1321 |
if (!xdr_bool(xdrsp, &false)) |
1359 |
if (!xdr_bool(xdrsp, &false)) |
1322 |
return (0); |
1360 |
return (0); |
Lines 1416-1425
static FILE *exp_file;
Link Here
|
1416 |
* Get the export list from one, currently open file |
1454 |
* Get the export list from one, currently open file |
1417 |
*/ |
1455 |
*/ |
1418 |
static void |
1456 |
static void |
1419 |
get_exportlist_one(void) |
1457 |
get_exportlist_one(int passno) |
1420 |
{ |
1458 |
{ |
1421 |
struct exportlist *ep; |
1459 |
struct exportlist *ep; |
1422 |
struct grouplist *grp, *tgrp; |
1460 |
struct grouplist *grp, *tgrp, *savgrp; |
1423 |
struct dirlist *dirhead; |
1461 |
struct dirlist *dirhead; |
1424 |
struct statfs fsb; |
1462 |
struct statfs fsb; |
1425 |
struct xucred anon; |
1463 |
struct xucred anon; |
Lines 1540-1546
get_exportlist_one(void)
Link Here
|
1540 |
* See if this directory is already |
1578 |
* See if this directory is already |
1541 |
* in the list. |
1579 |
* in the list. |
1542 |
*/ |
1580 |
*/ |
1543 |
ep = ex_search(&fsb.f_fsid); |
1581 |
ep = ex_search(&fsb.f_fsid, exphead); |
1544 |
if (ep == (struct exportlist *)NULL) { |
1582 |
if (ep == (struct exportlist *)NULL) { |
1545 |
ep = get_exp(); |
1583 |
ep = get_exp(); |
1546 |
ep->ex_fs = fsb.f_fsid; |
1584 |
ep->ex_fs = fsb.f_fsid; |
Lines 1653-1663
get_exportlist_one(void)
Link Here
|
1653 |
* Loop through hosts, pushing the exports into the kernel. |
1691 |
* Loop through hosts, pushing the exports into the kernel. |
1654 |
* After loop, tgrp points to the start of the list and |
1692 |
* After loop, tgrp points to the start of the list and |
1655 |
* grp points to the last entry in the list. |
1693 |
* grp points to the last entry in the list. |
|
|
1694 |
* Do not do the do_mount() for passno == 1, since the |
1695 |
* second pass will do it, as required. |
1656 |
*/ |
1696 |
*/ |
1657 |
grp = tgrp; |
1697 |
grp = tgrp; |
1658 |
do { |
1698 |
do { |
1659 |
if (do_mount(ep, grp, exflags, &anon, dirp, dirplen, |
1699 |
grp->gr_exflags = exflags; |
1660 |
&fsb)) { |
1700 |
grp->gr_anon = anon; |
|
|
1701 |
if (passno == 0 && do_mount(ep, grp, exflags, &anon, |
1702 |
dirp, dirplen, &fsb, ep->ex_numsecflavors, |
1703 |
ep->ex_secflavors)) { |
1661 |
getexp_err(ep, tgrp, NULL); |
1704 |
getexp_err(ep, tgrp, NULL); |
1662 |
goto nextline; |
1705 |
goto nextline; |
1663 |
} |
1706 |
} |
Lines 1668-1676
get_exportlist_one(void)
Link Here
|
1668 |
*/ |
1711 |
*/ |
1669 |
if (v4root_phase > 0 && v4root_phase <= 2) { |
1712 |
if (v4root_phase > 0 && v4root_phase <= 2) { |
1670 |
/* |
1713 |
/* |
1671 |
* Since these structures aren't used by mountd, |
1714 |
* These structures are used for the "-I" reload, |
|
|
1715 |
* so save them for that case. Otherwise, just |
1672 |
* free them up now. |
1716 |
* free them up now. |
1673 |
*/ |
1717 |
*/ |
|
|
1718 |
if (passno == 1 && ep != NULL) { |
1719 |
savgrp = tgrp; |
1720 |
while (tgrp != NULL) { |
1721 |
/* |
1722 |
* Save the security flavors and exflags |
1723 |
* for this host set in the groups. |
1724 |
*/ |
1725 |
tgrp->gr_numsecflavors = |
1726 |
ep->ex_numsecflavors; |
1727 |
if (ep->ex_numsecflavors > 0) |
1728 |
memcpy(tgrp->gr_secflavors, |
1729 |
ep->ex_secflavors, |
1730 |
sizeof(ep->ex_secflavors)); |
1731 |
tgrp = tgrp->gr_next; |
1732 |
} |
1733 |
if (v4root_ep == NULL) { |
1734 |
v4root_ep = ep; |
1735 |
ep = NULL; /* Don't free below. */ |
1736 |
} |
1737 |
grp->gr_next = v4root_ep->ex_grphead; |
1738 |
v4root_ep->ex_grphead = savgrp; |
1739 |
} |
1674 |
if (ep != NULL) |
1740 |
if (ep != NULL) |
1675 |
free_exp(ep); |
1741 |
free_exp(ep); |
1676 |
while (tgrp != NULL) { |
1742 |
while (tgrp != NULL) { |
Lines 1685-1701
get_exportlist_one(void)
Link Here
|
1685 |
* Success. Update the data structures. |
1751 |
* Success. Update the data structures. |
1686 |
*/ |
1752 |
*/ |
1687 |
if (has_host) { |
1753 |
if (has_host) { |
1688 |
hang_dirp(dirhead, tgrp, ep, opt_flags); |
1754 |
hang_dirp(dirhead, tgrp, ep, opt_flags, &anon, exflags); |
1689 |
grp->gr_next = grphead; |
1755 |
grp->gr_next = ep->ex_grphead; |
1690 |
grphead = tgrp; |
1756 |
ep->ex_grphead = tgrp; |
1691 |
} else { |
1757 |
} else { |
1692 |
hang_dirp(dirhead, (struct grouplist *)NULL, ep, |
1758 |
hang_dirp(dirhead, (struct grouplist *)NULL, ep, |
1693 |
opt_flags); |
1759 |
opt_flags, &anon, exflags); |
1694 |
free_grp(grp); |
1760 |
free_grp(grp); |
1695 |
} |
1761 |
} |
1696 |
dirhead = (struct dirlist *)NULL; |
1762 |
dirhead = (struct dirlist *)NULL; |
1697 |
if ((ep->ex_flag & EX_LINKED) == 0) { |
1763 |
if ((ep->ex_flag & EX_LINKED) == 0) { |
1698 |
SLIST_INSERT_HEAD(&exphead, ep, entries); |
1764 |
insert_exports(ep, exphead); |
1699 |
|
1765 |
|
1700 |
ep->ex_flag |= EX_LINKED; |
1766 |
ep->ex_flag |= EX_LINKED; |
1701 |
} |
1767 |
} |
Lines 1712-1758
nextline:
Link Here
|
1712 |
* Get the export list from all specified files |
1778 |
* Get the export list from all specified files |
1713 |
*/ |
1779 |
*/ |
1714 |
static void |
1780 |
static void |
1715 |
get_exportlist(void) |
1781 |
get_exportlist(int passno) |
1716 |
{ |
1782 |
{ |
1717 |
struct exportlist *ep, *ep2; |
|
|
1718 |
struct grouplist *grp, *tgrp; |
1719 |
struct export_args export; |
1783 |
struct export_args export; |
1720 |
struct iovec *iov; |
1784 |
struct iovec *iov; |
1721 |
struct statfs *fsp, *mntbufp; |
1785 |
struct statfs *mntbufp; |
1722 |
struct xvfsconf vfc; |
|
|
1723 |
char errmsg[255]; |
1786 |
char errmsg[255]; |
1724 |
int num, i; |
1787 |
int num, i; |
1725 |
int iovlen; |
1788 |
int iovlen; |
1726 |
int done; |
|
|
1727 |
struct nfsex_args eargs; |
1789 |
struct nfsex_args eargs; |
1728 |
|
1790 |
|
1729 |
if (suspend_nfsd != 0) |
1791 |
MOUNTD_PASSNO("passno", passno); |
1730 |
(void)nfssvc(NFSSVC_SUSPENDNFSD, NULL); |
|
|
1731 |
v4root_dirpath[0] = '\0'; |
1792 |
v4root_dirpath[0] = '\0'; |
|
|
1793 |
free_v4rootexp(); |
1794 |
if (passno == 1) { |
1795 |
/* |
1796 |
* Save the current lists as old ones, so that the new lists |
1797 |
* can be compared with the old ones in the 2nd pass. |
1798 |
*/ |
1799 |
for (i = 0; i < exphashsize; i++) { |
1800 |
SLIST_FIRST(&oldexphead[i]) = SLIST_FIRST(&exphead[i]); |
1801 |
SLIST_INIT(&exphead[i]); |
1802 |
} |
1803 |
|
1804 |
/* Note that the public fh has not yet been set. */ |
1805 |
has_set_publicfh = 0; |
1806 |
|
1807 |
/* Read the export file(s) and process them */ |
1808 |
read_exportfile(passno); |
1809 |
} else { |
1810 |
/* |
1811 |
* Just make the old lists empty. |
1812 |
* exphashsize == 0 for the first call, before oldexphead |
1813 |
* has been initialized-->loop won't be executed. |
1814 |
*/ |
1815 |
for (i = 0; i < exphashsize; i++) |
1816 |
SLIST_INIT(&oldexphead[i]); |
1817 |
} |
1818 |
|
1732 |
bzero(&export, sizeof(export)); |
1819 |
bzero(&export, sizeof(export)); |
1733 |
export.ex_flags = MNT_DELEXPORT; |
1820 |
export.ex_flags = MNT_DELEXPORT; |
1734 |
iov = NULL; |
1821 |
iov = NULL; |
1735 |
iovlen = 0; |
1822 |
iovlen = 0; |
1736 |
bzero(errmsg, sizeof(errmsg)); |
1823 |
bzero(errmsg, sizeof(errmsg)); |
1737 |
|
1824 |
|
|
|
1825 |
if (suspend_nfsd != 0) |
1826 |
(void)nfssvc(NFSSVC_SUSPENDNFSD, NULL); |
1738 |
/* |
1827 |
/* |
1739 |
* First, get rid of the old list |
1828 |
* Delete the old V4 root dir. |
1740 |
*/ |
|
|
1741 |
SLIST_FOREACH_SAFE(ep, &exphead, entries, ep2) { |
1742 |
SLIST_REMOVE(&exphead, ep, exportlist, entries); |
1743 |
free_exp(ep); |
1744 |
} |
1745 |
|
1746 |
grp = grphead; |
1747 |
while (grp) { |
1748 |
tgrp = grp; |
1749 |
grp = grp->gr_next; |
1750 |
free_grp(tgrp); |
1751 |
} |
1752 |
grphead = (struct grouplist *)NULL; |
1753 |
|
1754 |
/* |
1755 |
* and the old V4 root dir. |
1756 |
*/ |
1829 |
*/ |
1757 |
bzero(&eargs, sizeof (eargs)); |
1830 |
bzero(&eargs, sizeof (eargs)); |
1758 |
eargs.export.ex_flags = MNT_DELEXPORT; |
1831 |
eargs.export.ex_flags = MNT_DELEXPORT; |
Lines 1760-1826
get_exportlist(void)
Link Here
|
1760 |
errno != ENOENT) |
1833 |
errno != ENOENT) |
1761 |
syslog(LOG_ERR, "Can't delete exports for V4:"); |
1834 |
syslog(LOG_ERR, "Can't delete exports for V4:"); |
1762 |
|
1835 |
|
1763 |
/* |
1836 |
build_iovec(&iov, &iovlen, "fstype", NULL, 0); |
1764 |
* and clear flag that notes if a public fh has been exported. |
1837 |
build_iovec(&iov, &iovlen, "fspath", NULL, 0); |
1765 |
*/ |
1838 |
build_iovec(&iov, &iovlen, "from", NULL, 0); |
1766 |
has_publicfh = 0; |
1839 |
build_iovec(&iov, &iovlen, "update", NULL, 0); |
|
|
1840 |
build_iovec(&iov, &iovlen, "export", &export, |
1841 |
sizeof(export)); |
1842 |
build_iovec(&iov, &iovlen, "errmsg", errmsg, |
1843 |
sizeof(errmsg)); |
1767 |
|
1844 |
|
1768 |
/* |
1845 |
/* |
1769 |
* And delete exports that are in the kernel for all local |
1846 |
* For passno == 1, compare the old and new lists updating the kernel |
1770 |
* filesystems. |
1847 |
* exports for any cases that have changed. |
1771 |
* XXX: Should know how to handle all local exportable filesystems. |
1848 |
* This call is doing the second pass through the lists. |
|
|
1849 |
* If it fails, fall back on the bulk reload. |
1772 |
*/ |
1850 |
*/ |
1773 |
num = getmntinfo(&mntbufp, MNT_NOWAIT); |
1851 |
if (passno == 1 && compare_nmount_exportlist(iov, iovlen, errmsg) == |
1774 |
|
1852 |
0) { |
1775 |
if (num > 0) { |
1853 |
MOUNTD_COMPAREOK("compareok"); |
1776 |
build_iovec(&iov, &iovlen, "fstype", NULL, 0); |
1854 |
/* Free up the old lists. */ |
1777 |
build_iovec(&iov, &iovlen, "fspath", NULL, 0); |
1855 |
free_exports(oldexphead); |
1778 |
build_iovec(&iov, &iovlen, "from", NULL, 0); |
1856 |
} else { |
1779 |
build_iovec(&iov, &iovlen, "update", NULL, 0); |
1857 |
MOUNTD_PASSNO_0("doing passno=0"); |
1780 |
build_iovec(&iov, &iovlen, "export", &export, sizeof(export)); |
1858 |
/* |
1781 |
build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); |
1859 |
* Clear flag that notes if a public fh has been exported. |
1782 |
} |
1860 |
* It is set by do_mount() if MNT_EXPUBLIC is set for the entry. |
|
|
1861 |
*/ |
1862 |
has_publicfh = 0; |
1783 |
|
1863 |
|
1784 |
for (i = 0; i < num; i++) { |
1864 |
/* exphead == NULL if not yet allocated (first call). */ |
1785 |
fsp = &mntbufp[i]; |
1865 |
if (exphead != NULL) { |
1786 |
if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) { |
1866 |
/* |
1787 |
syslog(LOG_ERR, "getvfsbyname() failed for %s", |
1867 |
* First, get rid of the old lists. |
1788 |
fsp->f_fstypename); |
1868 |
*/ |
1789 |
continue; |
1869 |
free_exports(exphead); |
|
|
1870 |
free_exports(oldexphead); |
1790 |
} |
1871 |
} |
1791 |
|
1872 |
|
1792 |
/* |
1873 |
/* |
1793 |
* We do not need to delete "export" flag from |
1874 |
* And delete exports that are in the kernel for all local |
1794 |
* filesystems that do not have it set. |
1875 |
* filesystems. |
1795 |
*/ |
1876 |
* XXX: Should know how to handle all local exportable |
1796 |
if (!(fsp->f_flags & MNT_EXPORTED)) |
1877 |
* filesystems. |
1797 |
continue; |
1878 |
*/ |
1798 |
/* |
1879 |
num = getmntinfo(&mntbufp, MNT_NOWAIT); |
1799 |
* Do not delete export for network filesystem by |
1880 |
|
1800 |
* passing "export" arg to nmount(). |
1881 |
/* Allocate hash tables, for first call. */ |
1801 |
* It only makes sense to do this for local filesystems. |
1882 |
if (exphead == NULL) { |
1802 |
*/ |
1883 |
/* Target an average linked list length of 10. */ |
1803 |
if (vfc.vfc_flags & VFCF_NETWORK) |
1884 |
exphashsize = num / 10; |
1804 |
continue; |
1885 |
if (exphashsize < 1) |
1805 |
|
1886 |
exphashsize = 1; |
1806 |
iov[1].iov_base = fsp->f_fstypename; |
1887 |
else if (exphashsize > 100000) |
1807 |
iov[1].iov_len = strlen(fsp->f_fstypename) + 1; |
1888 |
exphashsize = 100000; |
1808 |
iov[3].iov_base = fsp->f_mntonname; |
1889 |
exphead = malloc(exphashsize * sizeof(*exphead)); |
1809 |
iov[3].iov_len = strlen(fsp->f_mntonname) + 1; |
1890 |
oldexphead = malloc(exphashsize * sizeof(*oldexphead)); |
1810 |
iov[5].iov_base = fsp->f_mntfromname; |
1891 |
if (exphead == NULL || oldexphead == NULL) |
1811 |
iov[5].iov_len = strlen(fsp->f_mntfromname) + 1; |
1892 |
errx(1, "Can't malloc hash tables"); |
1812 |
errmsg[0] = '\0'; |
1893 |
|
1813 |
|
1894 |
for (i = 0; i < exphashsize; i++) { |
1814 |
/* |
1895 |
SLIST_INIT(&exphead[i]); |
1815 |
* EXDEV is returned when path exists but is not a |
1896 |
SLIST_INIT(&oldexphead[i]); |
1816 |
* mount point. May happens if raced with unmount. |
1897 |
} |
1817 |
*/ |
|
|
1818 |
if (nmount(iov, iovlen, fsp->f_flags) < 0 && |
1819 |
errno != ENOENT && errno != ENOTSUP && errno != EXDEV) { |
1820 |
syslog(LOG_ERR, |
1821 |
"can't delete exports for %s: %m %s", |
1822 |
fsp->f_mntonname, errmsg); |
1823 |
} |
1898 |
} |
|
|
1899 |
|
1900 |
for (i = 0; i < num; i++) |
1901 |
delete_export(iov, iovlen, &mntbufp[i], errmsg); |
1902 |
|
1903 |
|
1904 |
/* Read the export file(s) and process them */ |
1905 |
read_exportfile(0); |
1824 |
} |
1906 |
} |
1825 |
|
1907 |
|
1826 |
if (iov != NULL) { |
1908 |
if (iov != NULL) { |
Lines 1838-1843
get_exportlist(void)
Link Here
|
1838 |
} |
1920 |
} |
1839 |
|
1921 |
|
1840 |
/* |
1922 |
/* |
|
|
1923 |
* If there was no public fh, clear any previous one set. |
1924 |
*/ |
1925 |
if (has_publicfh == 0) |
1926 |
(void) nfssvc(NFSSVC_NOPUBLICFH, NULL); |
1927 |
|
1928 |
/* Resume the nfsd. If they weren't suspended, this is harmless. */ |
1929 |
(void)nfssvc(NFSSVC_RESUMENFSD, NULL); |
1930 |
MOUNTD_EO_GET_EXPORTLIST("eo get exportlist"); |
1931 |
} |
1932 |
|
1933 |
/* |
1934 |
* Insert an export entry in the appropriate list. |
1935 |
*/ |
1936 |
static void |
1937 |
insert_exports(struct exportlist *ep, struct exportlisthead *exhp) |
1938 |
{ |
1939 |
uint32_t i; |
1940 |
|
1941 |
i = EXPHASH(&ep->ex_fs); |
1942 |
MOUNTD_FS_HASH("fs hash", ep->ex_fsdir, i); |
1943 |
SLIST_INSERT_HEAD(&exhp[i], ep, entries); |
1944 |
} |
1945 |
|
1946 |
/* |
1947 |
* Free up the exports lists passed in as arguments. |
1948 |
*/ |
1949 |
static void |
1950 |
free_exports(struct exportlisthead *exhp) |
1951 |
{ |
1952 |
struct exportlist *ep, *ep2; |
1953 |
int i; |
1954 |
|
1955 |
for (i = 0; i < exphashsize; i++) { |
1956 |
SLIST_FOREACH_SAFE(ep, &exhp[i], entries, ep2) { |
1957 |
SLIST_REMOVE(&exhp[i], ep, exportlist, entries); |
1958 |
free_exp(ep); |
1959 |
} |
1960 |
SLIST_INIT(&exhp[i]); |
1961 |
} |
1962 |
} |
1963 |
|
1964 |
/* |
1965 |
* Read the exports file(s) and call get_exportlist_one() for each line. |
1966 |
*/ |
1967 |
static void |
1968 |
read_exportfile(int passno) |
1969 |
{ |
1970 |
int done, i; |
1971 |
|
1972 |
/* |
1841 |
* Read in the exports file and build the list, calling |
1973 |
* Read in the exports file and build the list, calling |
1842 |
* nmount() as we go along to push the export rules into the kernel. |
1974 |
* nmount() as we go along to push the export rules into the kernel. |
1843 |
*/ |
1975 |
*/ |
Lines 1849-1855
get_exportlist(void)
Link Here
|
1849 |
syslog(LOG_WARNING, "can't open %s", exnames[i]); |
1981 |
syslog(LOG_WARNING, "can't open %s", exnames[i]); |
1850 |
continue; |
1982 |
continue; |
1851 |
} |
1983 |
} |
1852 |
get_exportlist_one(); |
1984 |
get_exportlist_one(passno); |
1853 |
fclose(exp_file); |
1985 |
fclose(exp_file); |
1854 |
done++; |
1986 |
done++; |
1855 |
} |
1987 |
} |
Lines 1857-1871
get_exportlist(void)
Link Here
|
1857 |
syslog(LOG_ERR, "can't open any exports file"); |
1989 |
syslog(LOG_ERR, "can't open any exports file"); |
1858 |
exit(2); |
1990 |
exit(2); |
1859 |
} |
1991 |
} |
|
|
1992 |
} |
1993 |
|
1994 |
/* |
1995 |
* Compare the export lists against the old ones and do nmount() operations |
1996 |
* for any cases that have changed. This avoids doing nmount() for entries |
1997 |
* that have not changed. |
1998 |
* Return 0 upon success, 1 otherwise. |
1999 |
*/ |
2000 |
static int |
2001 |
compare_nmount_exportlist(struct iovec *iov, int iovlen, char *errmsg) |
2002 |
{ |
2003 |
struct exportlist *ep, *oep; |
2004 |
struct grouplist *grp; |
2005 |
struct statfs fs, ofs; |
2006 |
int i, ret; |
1860 |
|
2007 |
|
1861 |
/* |
2008 |
/* |
1862 |
* If there was no public fh, clear any previous one set. |
2009 |
* Loop through the current list and look for an entry in the old |
|
|
2010 |
* list. |
2011 |
* If found, check to see if it the same. |
2012 |
* If it is not the same, delete and re-export. |
2013 |
* Then mark it done on the old list. |
2014 |
* else (not found) |
2015 |
* export it. |
2016 |
* Any entries left in the old list after processing must have their |
2017 |
* exports deleted. |
1863 |
*/ |
2018 |
*/ |
1864 |
if (has_publicfh == 0) |
2019 |
for (i = 0; i < exphashsize; i++) |
1865 |
(void) nfssvc(NFSSVC_NOPUBLICFH, NULL); |
2020 |
SLIST_FOREACH(ep, &exphead[i], entries) { |
|
|
2021 |
MOUNTD_FOREACH_EP("foreach ep", ep->ex_fsdir); |
2022 |
oep = ex_search(&ep->ex_fs, oldexphead); |
2023 |
if (oep != NULL) { |
2024 |
/* |
2025 |
* Check the mount paths are the same. |
2026 |
* If not, return 1 so that the reload of the |
2027 |
* exports will be done in bulk, the |
2028 |
* passno == 0 way. |
2029 |
*/ |
2030 |
MOUNTD_FOUND_OLD_EXP("found old exp"); |
2031 |
if (strcmp(ep->ex_fsdir, oep->ex_fsdir) != 0) |
2032 |
return (1); |
2033 |
MOUNTD_SAME_FSDIR("same fsdir"); |
2034 |
/* |
2035 |
* Test to see if the entry is the same. |
2036 |
* If not the same delete exports and |
2037 |
* re-export. |
2038 |
*/ |
2039 |
if (compare_export(ep, oep) != 0) { |
2040 |
/* |
2041 |
* Clear has_publicfh if if was set |
2042 |
* in the old exports, but only if it |
2043 |
* has not been set during processing of |
2044 |
* the exports for this pass, as |
2045 |
* indicated by has_set_publicfh. |
2046 |
*/ |
2047 |
if (has_set_publicfh == 0 && |
2048 |
(oep->ex_flag & EX_PUBLICFH) != 0) |
2049 |
has_publicfh = 0; |
1866 |
|
2050 |
|
1867 |
/* Resume the nfsd. If they weren't suspended, this is harmless. */ |
2051 |
/* Delete and re-export. */ |
1868 |
(void)nfssvc(NFSSVC_RESUMENFSD, NULL); |
2052 |
if (statfs(ep->ex_fsdir, &fs) < 0) |
|
|
2053 |
return (1); |
2054 |
delete_export(iov, iovlen, &fs, errmsg); |
2055 |
ret = do_export_mount(ep, &fs); |
2056 |
if (ret != 0) |
2057 |
return (ret); |
2058 |
} |
2059 |
oep->ex_flag |= EX_DONE; |
2060 |
MOUNTD_EXDONE("exdone"); |
2061 |
} else { |
2062 |
MOUNTD_NOT_FOUND_SO_EXPORT( |
2063 |
"not found so export"); |
2064 |
/* Not found, so do export. */ |
2065 |
if (statfs(ep->ex_fsdir, &fs) < 0) |
2066 |
return (1); |
2067 |
ret = do_export_mount(ep, &fs); |
2068 |
if (ret != 0) |
2069 |
return (ret); |
2070 |
} |
2071 |
} |
2072 |
|
2073 |
/* Delete exports not done. */ |
2074 |
for (i = 0; i < exphashsize; i++) |
2075 |
SLIST_FOREACH(oep, &oldexphead[i], entries) { |
2076 |
if ((oep->ex_flag & EX_DONE) == 0) { |
2077 |
MOUNTD_NOT_DONE_DELETE("not done delete", |
2078 |
oep->ex_fsdir); |
2079 |
if (statfs(oep->ex_fsdir, &ofs) >= 0 && |
2080 |
oep->ex_fs.val[0] == ofs.f_fsid.val[0] && |
2081 |
oep->ex_fs.val[1] == ofs.f_fsid.val[1]) { |
2082 |
MOUNTD_DO_DELETE("do_delete"); |
2083 |
/* |
2084 |
* Clear has_publicfh if if was set |
2085 |
* in the old exports, but only if it |
2086 |
* has not been set during processing of |
2087 |
* the exports for this pass, as |
2088 |
* indicated by has_set_publicfh. |
2089 |
*/ |
2090 |
if (has_set_publicfh == 0 && |
2091 |
(oep->ex_flag & EX_PUBLICFH) != 0) |
2092 |
has_publicfh = 0; |
2093 |
|
2094 |
delete_export(iov, iovlen, &ofs, |
2095 |
errmsg); |
2096 |
} |
2097 |
} |
2098 |
} |
2099 |
|
2100 |
/* Do the V4 root exports, as required. */ |
2101 |
grp = NULL; |
2102 |
if (v4root_ep != NULL) |
2103 |
grp = v4root_ep->ex_grphead; |
2104 |
v4root_phase = 2; |
2105 |
while (v4root_ep != NULL && grp != NULL) { |
2106 |
MOUNTD_V4ROOT_EXPATH("v4root expath", v4root_dirpath); |
2107 |
ret = do_mount(v4root_ep, grp, grp->gr_exflags, &grp->gr_anon, |
2108 |
v4root_dirpath, strlen(v4root_dirpath), &fs, |
2109 |
grp->gr_numsecflavors, grp->gr_secflavors); |
2110 |
if (ret != 0) { |
2111 |
v4root_phase = 0; |
2112 |
return (ret); |
2113 |
} |
2114 |
grp = grp->gr_next; |
2115 |
} |
2116 |
v4root_phase = 0; |
2117 |
free_v4rootexp(); |
2118 |
return (0); |
2119 |
} |
2120 |
|
2121 |
/* |
2122 |
* Compare old and current exportlist entries for the fsid and return 0 |
2123 |
* if they are the same, 1 otherwise. |
2124 |
*/ |
2125 |
static int |
2126 |
compare_export(struct exportlist *ep, struct exportlist *oep) |
2127 |
{ |
2128 |
struct grouplist *grp, *ogrp; |
2129 |
|
2130 |
if (strcmp(ep->ex_fsdir, oep->ex_fsdir) != 0) |
2131 |
return (1); |
2132 |
if ((ep->ex_flag & EX_DEFSET) != (oep->ex_flag & EX_DEFSET)) |
2133 |
return (1); |
2134 |
if ((ep->ex_defdir != NULL && oep->ex_defdir == NULL) || |
2135 |
(ep->ex_defdir == NULL && oep->ex_defdir != NULL)) |
2136 |
return (1); |
2137 |
if (ep->ex_defdir != NULL && (ep->ex_defdir->dp_flag & DP_DEFSET) != |
2138 |
(oep->ex_defdir->dp_flag & DP_DEFSET)) |
2139 |
return (1); |
2140 |
if ((ep->ex_flag & EX_DEFSET) != 0 && (ep->ex_defnumsecflavors != |
2141 |
oep->ex_defnumsecflavors || ep->ex_defexflags != |
2142 |
oep->ex_defexflags || compare_cred(&ep->ex_defanon, |
2143 |
&oep->ex_defanon) != 0 || compare_secflavor(ep->ex_defsecflavors, |
2144 |
oep->ex_defsecflavors, ep->ex_defnumsecflavors) != 0)) |
2145 |
return (1); |
2146 |
|
2147 |
/* Now, check all the groups. */ |
2148 |
for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next) |
2149 |
ogrp->gr_flag = 0; |
2150 |
for (grp = ep->ex_grphead; grp != NULL; grp = grp->gr_next) { |
2151 |
for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = |
2152 |
ogrp->gr_next) |
2153 |
if ((ogrp->gr_flag & GR_FND) == 0 && |
2154 |
grp->gr_numsecflavors == ogrp->gr_numsecflavors && |
2155 |
grp->gr_exflags == ogrp->gr_exflags && |
2156 |
compare_cred(&grp->gr_anon, &ogrp->gr_anon) == 0 && |
2157 |
compare_secflavor(grp->gr_secflavors, |
2158 |
ogrp->gr_secflavors, grp->gr_numsecflavors) == 0) |
2159 |
break; |
2160 |
if (ogrp != NULL) |
2161 |
ogrp->gr_flag |= GR_FND; |
2162 |
else |
2163 |
return (1); |
2164 |
} |
2165 |
for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next) |
2166 |
if ((ogrp->gr_flag & GR_FND) == 0) |
2167 |
return (1); |
2168 |
return (0); |
2169 |
} |
2170 |
|
2171 |
/* |
2172 |
* Compare to struct xucred's. Return 0 if the same and 1 otherwise. |
2173 |
*/ |
2174 |
static int |
2175 |
compare_cred(struct xucred *cr0, struct xucred *cr1) |
2176 |
{ |
2177 |
int fnd, grp_fnd[XU_NGROUPS], i, j; |
2178 |
|
2179 |
if (cr0->cr_uid != cr1->cr_uid || cr0->cr_ngroups != cr1->cr_ngroups) |
2180 |
return (1); |
2181 |
|
2182 |
/* Handle common cases. */ |
2183 |
if (cr0->cr_ngroups == 0) |
2184 |
return (0); |
2185 |
if (cr0->cr_ngroups == 1) { |
2186 |
if (cr0->cr_groups[0] == cr1->cr_groups[0]) |
2187 |
return (0); |
2188 |
return (1); |
2189 |
} |
2190 |
if (memcmp(cr0->cr_groups, cr1->cr_groups, sizeof(*cr0->cr_groups) * |
2191 |
cr0->cr_ngroups) == 0) |
2192 |
return (0); |
2193 |
|
2194 |
for (i = 0; i < cr0->cr_ngroups; i++) |
2195 |
grp_fnd[i] = 0; |
2196 |
/* |
2197 |
* Search through the old and new groups lists, noting matches. |
2198 |
* Since there is no ordering and duplicates can exist in the lists, |
2199 |
* the algorithm is inefficient. However cr_ngroups is small and |
2200 |
* the common cases are handled above. |
2201 |
*/ |
2202 |
for (i = 0; i < cr0->cr_ngroups; i++) { |
2203 |
fnd = 0; |
2204 |
for (j = 0; j < cr0->cr_ngroups; j++) { |
2205 |
if (cr0->cr_groups[i] == cr1->cr_groups[j]) { |
2206 |
grp_fnd[j] = 1; |
2207 |
fnd = 1; |
2208 |
} |
2209 |
} |
2210 |
if (fnd == 0) |
2211 |
return (1); |
2212 |
} |
2213 |
for (i = 0; i < cr0->cr_ngroups; i++) |
2214 |
if (grp_fnd[i] == 0) |
2215 |
return (1); |
2216 |
return (0); |
2217 |
} |
2218 |
|
2219 |
/* |
2220 |
* Compare two lists of security flavors. Return 0 if the same and 1 otherwise. |
2221 |
* This is almost identical to compare_cred(), but I didn't factor out |
2222 |
* the algorithm, since compare_cred() may need to change if/when more than |
2223 |
* XU_NGROUPS groups are supported. |
2224 |
*/ |
2225 |
static int |
2226 |
compare_secflavor(int *sec1, int *sec2, int nsec) |
2227 |
{ |
2228 |
int fnd, sec_fnd[MAXSECFLAVORS], i, j; |
2229 |
|
2230 |
/* Handle common cases first. */ |
2231 |
if (nsec == 0) |
2232 |
return (0); |
2233 |
if (nsec == 1) { |
2234 |
if (sec1[0] == sec2[0]) |
2235 |
return (0); |
2236 |
return (1); |
2237 |
} |
2238 |
if (memcmp(sec1, sec2, sizeof(*sec1) * nsec) == 0) |
2239 |
return (0); |
2240 |
|
2241 |
for (i = 0; i < nsec; i++) |
2242 |
sec_fnd[i] = 0; |
2243 |
/* |
2244 |
* Search the new group list starting at the same position in the |
2245 |
* Search through the old and new secflavors lists, noting matches. |
2246 |
* Since there is no ordering and duplicates can exist in the lists, |
2247 |
* the algorithm is inefficient. However nsec is small and |
2248 |
* the common cases are handled above. |
2249 |
*/ |
2250 |
for (i = 0; i < nsec; i++) { |
2251 |
fnd = 0; |
2252 |
for (j = 0; j < nsec; j++) { |
2253 |
if (sec1[i] == sec2[j]) { |
2254 |
sec_fnd[j] = 1; |
2255 |
fnd = 1; |
2256 |
} |
2257 |
} |
2258 |
if (fnd == 0) |
2259 |
return (1); |
2260 |
} |
2261 |
for (i = 0; i < nsec; i++) |
2262 |
if (sec_fnd[i] == 0) |
2263 |
return (1); |
2264 |
return (0); |
2265 |
} |
2266 |
|
2267 |
/* |
2268 |
* Delete an exports entry. |
2269 |
*/ |
2270 |
static void |
2271 |
delete_export(struct iovec *iov, int iovlen, struct statfs *fsp, char *errmsg) |
2272 |
{ |
2273 |
struct xvfsconf vfc; |
2274 |
|
2275 |
if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) { |
2276 |
syslog(LOG_ERR, "getvfsbyname() failed for %s", |
2277 |
fsp->f_fstypename); |
2278 |
return; |
2279 |
} |
2280 |
|
2281 |
/* |
2282 |
* We do not need to delete "export" flag from |
2283 |
* filesystems that do not have it set. |
2284 |
*/ |
2285 |
if (!(fsp->f_flags & MNT_EXPORTED)) |
2286 |
return; |
2287 |
/* |
2288 |
* Do not delete export for network filesystem by |
2289 |
* passing "export" arg to nmount(). |
2290 |
* It only makes sense to do this for local filesystems. |
2291 |
*/ |
2292 |
if (vfc.vfc_flags & VFCF_NETWORK) |
2293 |
return; |
2294 |
|
2295 |
iov[1].iov_base = fsp->f_fstypename; |
2296 |
iov[1].iov_len = strlen(fsp->f_fstypename) + 1; |
2297 |
iov[3].iov_base = fsp->f_mntonname; |
2298 |
iov[3].iov_len = strlen(fsp->f_mntonname) + 1; |
2299 |
iov[5].iov_base = fsp->f_mntfromname; |
2300 |
iov[5].iov_len = strlen(fsp->f_mntfromname) + 1; |
2301 |
errmsg[0] = '\0'; |
2302 |
|
2303 |
/* |
2304 |
* EXDEV is returned when path exists but is not a |
2305 |
* mount point. May happens if raced with unmount. |
2306 |
*/ |
2307 |
if (nmount(iov, iovlen, fsp->f_flags) < 0 && errno != ENOENT && |
2308 |
errno != ENOTSUP && errno != EXDEV) { |
2309 |
syslog(LOG_ERR, |
2310 |
"can't delete exports for %s: %m %s", |
2311 |
fsp->f_mntonname, errmsg); |
2312 |
} |
1869 |
} |
2313 |
} |
1870 |
|
2314 |
|
1871 |
/* |
2315 |
/* |
Lines 1924-1934
getexp_err(struct exportlist *ep, struct
Link Here
|
1924 |
* Search the export list for a matching fs. |
2368 |
* Search the export list for a matching fs. |
1925 |
*/ |
2369 |
*/ |
1926 |
static struct exportlist * |
2370 |
static struct exportlist * |
1927 |
ex_search(fsid_t *fsid) |
2371 |
ex_search(fsid_t *fsid, struct exportlisthead *exhp) |
1928 |
{ |
2372 |
{ |
1929 |
struct exportlist *ep; |
2373 |
struct exportlist *ep; |
|
|
2374 |
uint32_t i; |
1930 |
|
2375 |
|
1931 |
SLIST_FOREACH(ep, &exphead, entries) { |
2376 |
i = EXPHASH(fsid); |
|
|
2377 |
SLIST_FOREACH(ep, &exhp[i], entries) { |
1932 |
if (ep->ex_fs.val[0] == fsid->val[0] && |
2378 |
if (ep->ex_fs.val[0] == fsid->val[0] && |
1933 |
ep->ex_fs.val[1] == fsid->val[1]) |
2379 |
ep->ex_fs.val[1] == fsid->val[1]) |
1934 |
return (ep); |
2380 |
return (ep); |
Lines 1965-1971
add_expdir(struct dirlist **dpp, char *c
Link Here
|
1965 |
*/ |
2411 |
*/ |
1966 |
static void |
2412 |
static void |
1967 |
hang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep, |
2413 |
hang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep, |
1968 |
int flags) |
2414 |
int flags, struct xucred *anoncrp, int exflags) |
1969 |
{ |
2415 |
{ |
1970 |
struct hostlist *hp; |
2416 |
struct hostlist *hp; |
1971 |
struct dirlist *dp2; |
2417 |
struct dirlist *dp2; |
Lines 1976-1987
hang_dirp(struct dirlist *dp, struct gro
Link Here
|
1976 |
else |
2422 |
else |
1977 |
ep->ex_defdir = dp; |
2423 |
ep->ex_defdir = dp; |
1978 |
if (grp == (struct grouplist *)NULL) { |
2424 |
if (grp == (struct grouplist *)NULL) { |
|
|
2425 |
ep->ex_flag |= EX_DEFSET; |
1979 |
ep->ex_defdir->dp_flag |= DP_DEFSET; |
2426 |
ep->ex_defdir->dp_flag |= DP_DEFSET; |
1980 |
/* Save the default security flavors list. */ |
2427 |
/* Save the default security flavors list. */ |
1981 |
ep->ex_defnumsecflavors = ep->ex_numsecflavors; |
2428 |
ep->ex_defnumsecflavors = ep->ex_numsecflavors; |
1982 |
if (ep->ex_numsecflavors > 0) |
2429 |
if (ep->ex_numsecflavors > 0) |
1983 |
memcpy(ep->ex_defsecflavors, ep->ex_secflavors, |
2430 |
memcpy(ep->ex_defsecflavors, ep->ex_secflavors, |
1984 |
sizeof(ep->ex_secflavors)); |
2431 |
sizeof(ep->ex_secflavors)); |
|
|
2432 |
ep->ex_defanon = *anoncrp; |
2433 |
ep->ex_defexflags = exflags; |
1985 |
} else while (grp) { |
2434 |
} else while (grp) { |
1986 |
hp = get_ht(); |
2435 |
hp = get_ht(); |
1987 |
hp->ht_grp = grp; |
2436 |
hp->ht_grp = grp; |
Lines 2001-2007
hang_dirp(struct dirlist *dp, struct gro
Link Here
|
2001 |
*/ |
2450 |
*/ |
2002 |
while (dp) { |
2451 |
while (dp) { |
2003 |
dp2 = dp->dp_left; |
2452 |
dp2 = dp->dp_left; |
2004 |
add_dlist(&ep->ex_dirl, dp, grp, flags, ep); |
2453 |
add_dlist(&ep->ex_dirl, dp, grp, flags, ep, anoncrp, |
|
|
2454 |
exflags); |
2005 |
dp = dp2; |
2455 |
dp = dp2; |
2006 |
} |
2456 |
} |
2007 |
} |
2457 |
} |
Lines 2013-2019
hang_dirp(struct dirlist *dp, struct gro
Link Here
|
2013 |
*/ |
2463 |
*/ |
2014 |
static void |
2464 |
static void |
2015 |
add_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp, |
2465 |
add_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp, |
2016 |
int flags, struct exportlist *ep) |
2466 |
int flags, struct exportlist *ep, struct xucred *anoncrp, int exflags) |
2017 |
{ |
2467 |
{ |
2018 |
struct dirlist *dp; |
2468 |
struct dirlist *dp; |
2019 |
struct hostlist *hp; |
2469 |
struct hostlist *hp; |
Lines 2023-2032
add_dlist(struct dirlist **dpp, struct d
Link Here
|
2023 |
if (dp) { |
2473 |
if (dp) { |
2024 |
cmp = strcmp(dp->dp_dirp, newdp->dp_dirp); |
2474 |
cmp = strcmp(dp->dp_dirp, newdp->dp_dirp); |
2025 |
if (cmp > 0) { |
2475 |
if (cmp > 0) { |
2026 |
add_dlist(&dp->dp_left, newdp, grp, flags, ep); |
2476 |
add_dlist(&dp->dp_left, newdp, grp, flags, ep, anoncrp, |
|
|
2477 |
exflags); |
2027 |
return; |
2478 |
return; |
2028 |
} else if (cmp < 0) { |
2479 |
} else if (cmp < 0) { |
2029 |
add_dlist(&dp->dp_right, newdp, grp, flags, ep); |
2480 |
add_dlist(&dp->dp_right, newdp, grp, flags, ep, anoncrp, |
|
|
2481 |
exflags); |
2030 |
return; |
2482 |
return; |
2031 |
} else |
2483 |
} else |
2032 |
free((caddr_t)newdp); |
2484 |
free((caddr_t)newdp); |
Lines 2053-2064
add_dlist(struct dirlist **dpp, struct d
Link Here
|
2053 |
grp = grp->gr_next; |
2505 |
grp = grp->gr_next; |
2054 |
} while (grp); |
2506 |
} while (grp); |
2055 |
} else { |
2507 |
} else { |
|
|
2508 |
ep->ex_flag |= EX_DEFSET; |
2056 |
dp->dp_flag |= DP_DEFSET; |
2509 |
dp->dp_flag |= DP_DEFSET; |
2057 |
/* Save the default security flavors list. */ |
2510 |
/* Save the default security flavors list. */ |
2058 |
ep->ex_defnumsecflavors = ep->ex_numsecflavors; |
2511 |
ep->ex_defnumsecflavors = ep->ex_numsecflavors; |
2059 |
if (ep->ex_numsecflavors > 0) |
2512 |
if (ep->ex_numsecflavors > 0) |
2060 |
memcpy(ep->ex_defsecflavors, ep->ex_secflavors, |
2513 |
memcpy(ep->ex_defsecflavors, ep->ex_secflavors, |
2061 |
sizeof(ep->ex_secflavors)); |
2514 |
sizeof(ep->ex_secflavors)); |
|
|
2515 |
ep->ex_defanon = *anoncrp; |
2516 |
ep->ex_defexflags = exflags; |
2062 |
} |
2517 |
} |
2063 |
} |
2518 |
} |
2064 |
|
2519 |
|
Lines 2403-2408
get_host(char *cp, struct grouplist *grp
Link Here
|
2403 |
static void |
2858 |
static void |
2404 |
free_exp(struct exportlist *ep) |
2859 |
free_exp(struct exportlist *ep) |
2405 |
{ |
2860 |
{ |
|
|
2861 |
struct grouplist *grp, *tgrp; |
2406 |
|
2862 |
|
2407 |
if (ep->ex_defdir) { |
2863 |
if (ep->ex_defdir) { |
2408 |
free_host(ep->ex_defdir->dp_hosts); |
2864 |
free_host(ep->ex_defdir->dp_hosts); |
Lines 2413-2422
free_exp(struct exportlist *ep)
Link Here
|
2413 |
if (ep->ex_indexfile) |
2869 |
if (ep->ex_indexfile) |
2414 |
free(ep->ex_indexfile); |
2870 |
free(ep->ex_indexfile); |
2415 |
free_dir(ep->ex_dirl); |
2871 |
free_dir(ep->ex_dirl); |
|
|
2872 |
grp = ep->ex_grphead; |
2873 |
while (grp) { |
2874 |
tgrp = grp; |
2875 |
grp = grp->gr_next; |
2876 |
free_grp(tgrp); |
2877 |
} |
2416 |
free((caddr_t)ep); |
2878 |
free((caddr_t)ep); |
2417 |
} |
2879 |
} |
2418 |
|
2880 |
|
2419 |
/* |
2881 |
/* |
|
|
2882 |
* Free up the v4root exports. |
2883 |
*/ |
2884 |
static void |
2885 |
free_v4rootexp(void) |
2886 |
{ |
2887 |
|
2888 |
if (v4root_ep != NULL) { |
2889 |
free_exp(v4root_ep); |
2890 |
v4root_ep = NULL; |
2891 |
} |
2892 |
} |
2893 |
|
2894 |
/* |
2420 |
* Free hosts. |
2895 |
* Free hosts. |
2421 |
*/ |
2896 |
*/ |
2422 |
static void |
2897 |
static void |
Lines 2456-2467
out_of_mem(void)
Link Here
|
2456 |
} |
2931 |
} |
2457 |
|
2932 |
|
2458 |
/* |
2933 |
/* |
|
|
2934 |
* Call do_mount() from the struct exportlist, for each case needed. |
2935 |
*/ |
2936 |
static int |
2937 |
do_export_mount(struct exportlist *ep, struct statfs *fsp) |
2938 |
{ |
2939 |
struct grouplist *grp, defgrp; |
2940 |
int ret; |
2941 |
size_t dirlen; |
2942 |
|
2943 |
MOUNTD_DO_MOUNT_EXPORT("do mount export", ep->ex_fsdir); |
2944 |
dirlen = strlen(ep->ex_fsdir); |
2945 |
if ((ep->ex_flag & EX_DEFSET) != 0) { |
2946 |
defgrp.gr_type = GT_DEFAULT; |
2947 |
defgrp.gr_next = NULL; |
2948 |
/* We have an entry for all other hosts/nets. */ |
2949 |
MOUNTD_EX_DEFEXFLAGS("ex defexflags", ep->ex_defexflags); |
2950 |
ret = do_mount(ep, &defgrp, ep->ex_defexflags, &ep->ex_defanon, |
2951 |
ep->ex_fsdir, dirlen, fsp, ep->ex_defnumsecflavors, |
2952 |
ep->ex_defsecflavors); |
2953 |
if (ret != 0) |
2954 |
return (ret); |
2955 |
} |
2956 |
|
2957 |
/* Do a mount for each group. */ |
2958 |
grp = ep->ex_grphead; |
2959 |
while (grp != NULL) { |
2960 |
MOUNTD_DO_MOUNT_GR_TYPE("do mount gr type", grp->gr_type, |
2961 |
grp->gr_exflags); |
2962 |
ret = do_mount(ep, grp, grp->gr_exflags, &grp->gr_anon, |
2963 |
ep->ex_fsdir, dirlen, fsp, grp->gr_numsecflavors, |
2964 |
grp->gr_secflavors); |
2965 |
if (ret != 0) |
2966 |
return (ret); |
2967 |
grp = grp->gr_next; |
2968 |
} |
2969 |
return (0); |
2970 |
} |
2971 |
|
2972 |
/* |
2459 |
* Do the nmount() syscall with the update flag to push the export info into |
2973 |
* Do the nmount() syscall with the update flag to push the export info into |
2460 |
* the kernel. |
2974 |
* the kernel. |
2461 |
*/ |
2975 |
*/ |
2462 |
static int |
2976 |
static int |
2463 |
do_mount(struct exportlist *ep, struct grouplist *grp, int exflags, |
2977 |
do_mount(struct exportlist *ep, struct grouplist *grp, int exflags, |
2464 |
struct xucred *anoncrp, char *dirp, int dirplen, struct statfs *fsb) |
2978 |
struct xucred *anoncrp, char *dirp, int dirplen, struct statfs *fsb, |
|
|
2979 |
int numsecflavors, int *secflavors) |
2465 |
{ |
2980 |
{ |
2466 |
struct statfs fsb1; |
2981 |
struct statfs fsb1; |
2467 |
struct addrinfo *ai; |
2982 |
struct addrinfo *ai; |
Lines 2487-2500
do_mount(struct exportlist *ep, struct g
Link Here
|
2487 |
bzero(errmsg, sizeof(errmsg)); |
3002 |
bzero(errmsg, sizeof(errmsg)); |
2488 |
eap->ex_flags = exflags; |
3003 |
eap->ex_flags = exflags; |
2489 |
eap->ex_anon = *anoncrp; |
3004 |
eap->ex_anon = *anoncrp; |
|
|
3005 |
MOUNTD_DO_MOUNT_EXFLAGS("do mount exflags", exflags); |
2490 |
eap->ex_indexfile = ep->ex_indexfile; |
3006 |
eap->ex_indexfile = ep->ex_indexfile; |
2491 |
if (grp->gr_type == GT_HOST) |
3007 |
if (grp->gr_type == GT_HOST) |
2492 |
ai = grp->gr_ptr.gt_addrinfo; |
3008 |
ai = grp->gr_ptr.gt_addrinfo; |
2493 |
else |
3009 |
else |
2494 |
ai = NULL; |
3010 |
ai = NULL; |
2495 |
eap->ex_numsecflavors = ep->ex_numsecflavors; |
3011 |
eap->ex_numsecflavors = numsecflavors; |
|
|
3012 |
MOUNTD_DO_MOUNT_NUMSEC("do mount numsec", numsecflavors); |
2496 |
for (i = 0; i < eap->ex_numsecflavors; i++) |
3013 |
for (i = 0; i < eap->ex_numsecflavors; i++) |
2497 |
eap->ex_secflavors[i] = ep->ex_secflavors[i]; |
3014 |
eap->ex_secflavors[i] = secflavors[i]; |
2498 |
if (eap->ex_numsecflavors == 0) { |
3015 |
if (eap->ex_numsecflavors == 0) { |
2499 |
eap->ex_numsecflavors = 1; |
3016 |
eap->ex_numsecflavors = 1; |
2500 |
eap->ex_secflavors[0] = AUTH_SYS; |
3017 |
eap->ex_secflavors[0] = AUTH_SYS; |
Lines 2658-2665
do_mount(struct exportlist *ep, struct g
Link Here
|
2658 |
else if (nfssvc(NFSSVC_PUBLICFH, (caddr_t)&fh) < 0) |
3175 |
else if (nfssvc(NFSSVC_PUBLICFH, (caddr_t)&fh) < 0) |
2659 |
syslog(LOG_ERR, |
3176 |
syslog(LOG_ERR, |
2660 |
"Can't set public fh for %s", public_name); |
3177 |
"Can't set public fh for %s", public_name); |
2661 |
else |
3178 |
else { |
2662 |
has_publicfh = 1; |
3179 |
has_publicfh = 1; |
|
|
3180 |
has_set_publicfh = 1; |
3181 |
ep->ex_flag |= EX_PUBLICFH; |
3182 |
} |
2663 |
} |
3183 |
} |
2664 |
skip: |
3184 |
skip: |
2665 |
if (ai != NULL) |
3185 |
if (ai != NULL) |
Lines 2824-2841
static void
Link Here
|
2824 |
nextfield(char **cp, char **endcp) |
3344 |
nextfield(char **cp, char **endcp) |
2825 |
{ |
3345 |
{ |
2826 |
char *p; |
3346 |
char *p; |
|
|
3347 |
char quot = 0; |
2827 |
|
3348 |
|
2828 |
p = *cp; |
3349 |
p = *cp; |
2829 |
while (*p == ' ' || *p == '\t') |
3350 |
while (*p == ' ' || *p == '\t') |
2830 |
p++; |
3351 |
p++; |
2831 |
if (*p == '\n' || *p == '\0') |
3352 |
*cp = p; |
2832 |
*cp = *endcp = p; |
3353 |
while (*p != '\0') { |
2833 |
else { |
3354 |
if (quot) { |
2834 |
*cp = p++; |
3355 |
if (*p == quot) |
2835 |
while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0') |
3356 |
quot = 0; |
2836 |
p++; |
3357 |
} else { |
2837 |
*endcp = p; |
3358 |
if (*p == '\\' && *(p + 1) != '\0') |
2838 |
} |
3359 |
p++; |
|
|
3360 |
else if (*p == '\'' || *p == '"') |
3361 |
quot = *p; |
3362 |
else if (*p == ' ' || *p == '\t') |
3363 |
break; |
3364 |
} |
3365 |
p++; |
3366 |
}; |
3367 |
*endcp = p; |
2839 |
} |
3368 |
} |
2840 |
|
3369 |
|
2841 |
/* |
3370 |
/* |
Lines 2907-2914
parsecred(char *namelist, struct xucred
Link Here
|
2907 |
/* |
3436 |
/* |
2908 |
* Get the user's password table entry. |
3437 |
* Get the user's password table entry. |
2909 |
*/ |
3438 |
*/ |
2910 |
names = strsep_quote(&namelist, " \t\n"); |
3439 |
names = namelist; |
2911 |
name = strsep(&names, ":"); |
3440 |
name = strsep_quote(&names, ":"); |
2912 |
/* Bug? name could be NULL here */ |
3441 |
/* Bug? name could be NULL here */ |
2913 |
if (isdigit(*name) || *name == '-') |
3442 |
if (isdigit(*name) || *name == '-') |
2914 |
pw = getpwuid(atoi(name)); |
3443 |
pw = getpwuid(atoi(name)); |
Lines 2952-2958
parsecred(char *namelist, struct xucred
Link Here
|
2952 |
} |
3481 |
} |
2953 |
cr->cr_ngroups = 0; |
3482 |
cr->cr_ngroups = 0; |
2954 |
while (names != NULL && *names != '\0' && cr->cr_ngroups < XU_NGROUPS) { |
3483 |
while (names != NULL && *names != '\0' && cr->cr_ngroups < XU_NGROUPS) { |
2955 |
name = strsep(&names, ":"); |
3484 |
name = strsep_quote(&names, ":"); |
2956 |
if (isdigit(*name) || *name == '-') { |
3485 |
if (isdigit(*name) || *name == '-') { |
2957 |
cr->cr_groups[cr->cr_ngroups++] = atoi(name); |
3486 |
cr->cr_groups[cr->cr_ngroups++] = atoi(name); |
2958 |
} else { |
3487 |
} else { |