2 * utils/exportfs/exportfs.c
4 * Export file systems to knfsd
6 * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
8 * Extensive changes, 1999, Neil Brown <neilb@cse.unsw.edu.au>
30 static void export_all(int verbose
);
31 static void exportfs(char *arg
, char *options
, int verbose
);
32 static void unexportfs(char *arg
, int verbose
);
33 static void exports_update(int verbose
);
34 static void dump(int verbose
);
35 static void error(nfs_export
*exp
, int err
);
36 static void usage(void);
37 static void validate_export(nfs_export
*exp
);
40 main(int argc
, char **argv
)
52 xlog_open("exportfs");
56 while ((c
= getopt(argc
, argv
, "aio:ruvf")) != EOF
) {
86 if (optind
!= argc
&& f_all
) {
87 fprintf(stderr
,"exportfs: extra arguments are not permitted with -a or -r.\n");
90 if (f_ignore
&& (f_all
|| ! f_export
)) {
91 fprintf(stderr
,"exportfs: -i not meaningful with -a, -r or -u.\n");
94 if (f_reexport
&& ! f_export
) {
95 fprintf(stderr
, "exportfs: -r and -u are incompatible.\n");
98 new_cache
= check_new_cache();
99 if (optind
== argc
&& ! f_all
) {
104 fprintf(stderr
, "exportfs: -f: only available with new cache controls: mount /proc/fs/nfsd first\n");
114 if (f_export
&& ! f_ignore
)
115 export_read(_PATH_EXPORTS
);
118 export_all(f_verbose
);
120 for (i
= optind
; i
< argc
; i
++)
121 exportfs(argv
[i
], options
, f_verbose
);
123 /* If we are unexporting everything, then
124 * don't care about what should be exported, as that
125 * may require DNS lookups..
127 if (! ( !f_export
&& f_all
)) {
128 /* note: xtab_*_read does not update entries if they already exist,
129 * so this will not lose new options
134 for (i
= optind
; i
< argc
; i
++)
135 unexportfs(argv
[i
], f_verbose
);
141 exports_update(f_verbose
);
145 cache_flush(force_flush
);
153 exports_update_one(nfs_export
*exp
, int verbose
)
155 /* check mountpoint option */
156 if (exp
->m_mayexport
&&
157 exp
->m_export
.e_mountpoint
&&
158 !is_mountpoint(exp
->m_export
.e_mountpoint
[0]?
159 exp
->m_export
.e_mountpoint
:
160 exp
->m_export
.e_path
)) {
161 printf("%s not exported as %s not a mountpoint.\n",
162 exp
->m_export
.e_path
, exp
->m_export
.e_mountpoint
);
163 exp
->m_mayexport
= 0;
165 if (exp
->m_mayexport
&& ((exp
->m_exported
<1) || exp
->m_changed
)) {
167 printf("%sexporting %s:%s to kernel\n",
168 exp
->m_exported
?"re":"",
169 exp
->m_client
->m_hostname
,
170 exp
->m_export
.e_path
);
171 if (!export_export(exp
))
174 if (exp
->m_exported
&& ! exp
->m_mayexport
) {
176 printf("unexporting %s:%s from kernel\n",
177 exp
->m_client
->m_hostname
,
178 exp
->m_export
.e_path
);
179 if (!export_unexport(exp
))
185 /* we synchronise intention with reality.
186 * entries with m_mayexport get exported
187 * entries with m_exported but not m_mayexport get unexported
188 * looking at m_client->m_type == MCL_FQDN and m_client->m_type == MCL_GSS only
191 exports_update(int verbose
)
195 for (exp
= exportlist
[MCL_FQDN
].p_head
; exp
; exp
=exp
->m_next
) {
196 exports_update_one(exp
, verbose
);
198 for (exp
= exportlist
[MCL_GSS
].p_head
; exp
; exp
=exp
->m_next
) {
199 exports_update_one(exp
, verbose
);
204 * export_all finds all entries and
205 * marks them xtabent and mayexport so that they get exported
208 export_all(int verbose
)
213 for (i
= 0; i
< MCL_MAXTYPES
; i
++) {
214 for (exp
= exportlist
[i
].p_head
; exp
; exp
= exp
->m_next
) {
216 printf("exporting %s:%s\n",
217 exp
->m_client
->m_hostname
,
218 exp
->m_export
.e_path
);
220 exp
->m_mayexport
= 1;
223 validate_export(exp
);
230 exportfs(char *arg
, char *options
, int verbose
)
232 struct exportent
*eep
;
234 struct hostent
*hp
= NULL
;
239 if ((path
= strchr(arg
, ':')) != NULL
)
242 if (!path
|| *path
!= '/') {
243 fprintf(stderr
, "Invalid exporting option: %s\n", arg
);
247 if ((htype
= client_gettype(hname
)) == MCL_FQDN
&&
248 (hp
= gethostbyname(hname
)) != NULL
) {
249 struct hostent
*hp2
= hostent_dup (hp
);
250 hp
= gethostbyaddr(hp2
->h_addr
, hp2
->h_length
,
254 hp
= hostent_dup(hp
);
257 exp
= export_find(hp
, path
);
260 exp
= export_lookup(hname
, path
, 0);
264 if (!(eep
= mkexportent(hname
, path
, options
)) ||
265 !(exp
= export_create(eep
, 0))) {
269 } else if (!updateexportent(&exp
->m_export
, options
)) {
275 printf("exporting %s:%s\n", exp
->m_client
->m_hostname
,
276 exp
->m_export
.e_path
);
278 exp
->m_mayexport
= 1;
281 validate_export(exp
);
286 unexportfs(char *arg
, int verbose
)
289 struct hostent
*hp
= NULL
;
294 if ((path
= strchr(arg
, ':')) != NULL
)
297 if (!path
|| *path
!= '/') {
298 fprintf(stderr
, "Invalid unexporting option: %s\n",
303 if ((htype
= client_gettype(hname
)) == MCL_FQDN
) {
304 if ((hp
= gethostbyname(hname
)) != 0) {
305 hp
= hostent_dup (hp
);
306 hname
= (char *) hp
->h_name
;
310 for (exp
= exportlist
[htype
].p_head
; exp
; exp
= exp
->m_next
) {
311 if (path
&& strcmp(path
, exp
->m_export
.e_path
))
313 if (htype
!= exp
->m_client
->m_type
)
315 if (htype
== MCL_FQDN
316 && !matchhostname(exp
->m_export
.e_hostname
,
319 if (htype
!= MCL_FQDN
320 && strcasecmp(exp
->m_export
.e_hostname
, hname
))
324 if (exp
->m_exported
) {
325 printf("unexporting %s:%s from kernel\n",
326 exp
->m_client
->m_hostname
,
327 exp
->m_export
.e_path
);
331 printf("unexporting %s:%s\n",
332 exp
->m_client
->m_hostname
,
333 exp
->m_export
.e_path
);
336 if (exp
->m_exported
&& !export_unexport(exp
))
340 exp
->m_mayexport
= 0;
346 static int can_test(void)
350 char *setup
= "nfsd 0.0.0.0 2147483647 -test-client-\n";
351 fd
= open("/proc/net/rpc/auth.unix.ip/channel", O_WRONLY
);
352 if ( fd
< 0) return 0;
353 n
= write(fd
, setup
, strlen(setup
));
357 fd
= open("/proc/net/rpc/nfsd.export/channel", O_WRONLY
);
358 if ( fd
< 0) return 0;
363 static int test_export(char *path
, int with_fsid
)
368 sprintf(buf
, "-test-client- %s 3 %d -1 -1 0\n",
370 with_fsid
? NFSEXP_FSID
: 0);
371 fd
= open("/proc/net/rpc/nfsd.export/channel", O_WRONLY
);
374 n
= write(fd
, buf
, strlen(buf
));
382 validate_export(nfs_export
*exp
)
384 /* Check that the given export point is potentially exportable.
385 * We just give warnings here, don't cause anything to fail.
386 * If a path doesn't exist, or is not a dir or file, give an warning
387 * otherwise trial-export to '-test-client-' and check for failure.
390 char *path
= exp
->m_export
.e_path
;
394 if (stat(path
, &stb
) < 0) {
395 fprintf(stderr
, "exportfs: Warning: %s does not exist\n",
399 if (!S_ISDIR(stb
.st_mode
) && !S_ISREG(stb
.st_mode
)) {
400 fprintf(stderr
, "exportfs: Warning: %s is neither "
401 "a directory nor a file.\n"
402 " remote access will fail\n", path
);
408 if (!statfs64(path
, &stf
) &&
409 (stf
.f_fsid
.__val
[0] || stf
.f_fsid
.__val
[1]))
412 if ((exp
->m_export
.e_flags
& NFSEXP_FSID
) || exp
->m_export
.e_uuid
||
414 if ( !test_export(path
, 1)) {
415 fprintf(stderr
, "exportfs: Warning: %s does not "
416 "support NFS export.\n",
420 } else if ( ! test_export(path
, 0)) {
421 if (test_export(path
, 1))
422 fprintf(stderr
, "exportfs: Warning: %s requires fsid= "
423 "for NFS export\n", path
);
425 fprintf(stderr
, "exportfs: Warning: %s does not "
426 "support NFS export.\n",
435 dumpopt(char c
, char *fmt
, ...)
450 struct exportent
*ep
;
454 for (htype
= 0; htype
< MCL_MAXTYPES
; htype
++) {
455 for (exp
= exportlist
[htype
].p_head
; exp
; exp
= exp
->m_next
) {
458 continue; /* neilb */
459 if (htype
== MCL_ANONYMOUS
)
462 hname
= ep
->e_hostname
;
463 if (strlen(ep
->e_path
) > 14)
464 printf("%-14s\n\t\t%s", ep
->e_path
, hname
);
466 printf("%-14s\t%s", ep
->e_path
, hname
);
472 if (ep
->e_flags
& NFSEXP_READONLY
)
473 c
= dumpopt(c
, "ro");
475 c
= dumpopt(c
, "rw");
476 if (ep
->e_flags
& NFSEXP_ASYNC
)
477 c
= dumpopt(c
, "async");
478 if (ep
->e_flags
& NFSEXP_GATHERED_WRITES
)
479 c
= dumpopt(c
, "wdelay");
480 if (ep
->e_flags
& NFSEXP_NOHIDE
)
481 c
= dumpopt(c
, "nohide");
482 if (ep
->e_flags
& NFSEXP_CROSSMOUNT
)
483 c
= dumpopt(c
, "crossmnt");
484 if (ep
->e_flags
& NFSEXP_INSECURE_PORT
)
485 c
= dumpopt(c
, "insecure");
486 if (ep
->e_flags
& NFSEXP_ROOTSQUASH
)
487 c
= dumpopt(c
, "root_squash");
489 c
= dumpopt(c
, "no_root_squash");
490 if (ep
->e_flags
& NFSEXP_ALLSQUASH
)
491 c
= dumpopt(c
, "all_squash");
492 if (ep
->e_flags
& NFSEXP_NOSUBTREECHECK
)
493 c
= dumpopt(c
, "no_subtree_check");
494 if (ep
->e_flags
& NFSEXP_NOAUTHNLM
)
495 c
= dumpopt(c
, "insecure_locks");
496 if (ep
->e_flags
& NFSEXP_NOACL
)
497 c
= dumpopt(c
, "no_acl");
498 if (ep
->e_flags
& NFSEXP_FSID
)
499 c
= dumpopt(c
, "fsid=%d", ep
->e_fsid
);
501 c
= dumpopt(c
, "fsid=%s", ep
->e_uuid
);
502 if (ep
->e_mountpoint
)
503 c
= dumpopt(c
, "mountpoint%s%s",
504 ep
->e_mountpoint
[0]?"=":"",
506 if (ep
->e_anonuid
!= 65534)
507 c
= dumpopt(c
, "anonuid=%d", ep
->e_anonuid
);
508 if (ep
->e_anongid
!= 65534)
509 c
= dumpopt(c
, "anongid=%d", ep
->e_anongid
);
510 switch(ep
->e_fslocmethod
) {
514 c
= dumpopt(c
, "refer=%s", ep
->e_fslocdata
);
517 c
= dumpopt(c
, "replicas=%s", ep
->e_fslocdata
);
521 c
= dumpopt(c
, "fsloc=stub");
525 secinfo_show(stdout
, ep
);
526 printf("%c\n", (c
!= '(')? ')' : ' ');
532 error(nfs_export
*exp
, int err
)
534 fprintf(stderr
, "%s:%s: %s\n", exp
->m_client
->m_hostname
,
535 exp
->m_export
.e_path
, strerror(err
));
541 fprintf(stderr
, "usage: exportfs [-aruv] [host:/path]\n");