0070b544d654400f9dbafa1127423e55609c5ea0
[nfs-utils.git] / utils / exportfs / exportfs.c
1 /*
2  * utils/exportfs/exportfs.c
3  *
4  * Export file systems to knfsd
5  *
6  * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
7  *
8  * Extensive changes, 1999, Neil Brown <neilb@cse.unsw.edu.au>
9  */
10
11 #ifdef HAVE_CONFIG_H
12 #include <config.h>
13 #endif
14
15 #include <sys/vfs.h>
16 #include <sys/stat.h>
17 #include <unistd.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <stdarg.h>
21 #include <getopt.h>
22 #include <netdb.h>
23 #include <errno.h>
24 #include "xmalloc.h"
25 #include "misc.h"
26 #include "nfslib.h"
27 #include "exportfs.h"
28 #include "xmalloc.h"
29 #include "xlog.h"
30
31 static void     export_all(int verbose);
32 static void     exportfs(char *arg, char *options, int verbose);
33 static void     unexportfs(char *arg, int verbose);
34 static void     exports_update(int verbose);
35 static void     dump(int verbose);
36 static void     error(nfs_export *exp, int err);
37 static void     usage(void);
38 static void     validate_export(nfs_export *exp);
39
40 int
41 main(int argc, char **argv)
42 {
43         char    *options = NULL;
44         int     f_export = 1;
45         int     f_all = 0;
46         int     f_verbose = 0;
47         int     f_reexport = 0;
48         int     f_ignore = 0;
49         int     i, c;
50         int     new_cache = 0;
51         int     force_flush = 0;
52
53         xlog_open("exportfs");
54
55         export_errno = 0;
56
57         while ((c = getopt(argc, argv, "aio:ruvf")) != EOF) {
58                 switch(c) {
59                 case 'a':
60                         f_all = 1;
61                         break;
62                 case 'i':
63                         f_ignore = 1;
64                         break;
65                 case 'o':
66                         options = optarg;
67                         break;
68                 case 'r':
69                         f_reexport = 1;
70                         f_all = 1;
71                         break;
72                 case 'u':
73                         f_export = 0;
74                         break;
75                 case 'v':
76                         f_verbose = 1;
77                         break;
78                 case 'f':
79                         force_flush = 1;
80                         break;
81                 default:
82                         usage();
83                         break;
84                 }
85         }
86
87         if (optind != argc && f_all) {
88                 fprintf(stderr,"exportfs: extra arguments are not permitted with -a or -r.\n");
89                 return 1;
90         }
91         if (f_ignore && (f_all || ! f_export)) {
92                 fprintf(stderr,"exportfs: -i not meaningful with -a, -r or -u.\n");
93                 return 1;
94         }
95         if (f_reexport && ! f_export) {
96                 fprintf(stderr, "exportfs: -r and -u are incompatible.\n");
97                 return 1;
98         }
99         new_cache = check_new_cache();
100         if (optind == argc && ! f_all) {
101                 if (force_flush) {
102                         if (new_cache)
103                                 cache_flush(1);
104                         else {
105                                 fprintf(stderr, "exportfs: -f: only available with new cache controls: mount /proc/fs/nfsd first\n");
106                                 exit(1);
107                         }
108                         return 0;
109                 } else {
110                         xtab_export_read();
111                         dump(f_verbose);
112                         return 0;
113                 }
114         }
115         if (f_export && ! f_ignore)
116                 export_read(_PATH_EXPORTS);
117         if (f_export) {
118                 if (f_all)
119                         export_all(f_verbose);
120                 else
121                         for (i = optind; i < argc ; i++)
122                                 exportfs(argv[i], options, f_verbose);
123         }
124         /* If we are unexporting everything, then
125          * don't care about what should be exported, as that
126          * may require DNS lookups..
127          */
128         if (! ( !f_export && f_all)) {
129                 /* note: xtab_*_read does not update entries if they already exist,
130                  * so this will not lose new options
131                  */
132                 if (!f_reexport)
133                         xtab_export_read();
134                 if (!f_export)
135                         for (i = optind ; i < argc ; i++)
136                                 unexportfs(argv[i], f_verbose);
137                 if (!new_cache)
138                         rmtab_read();
139         }
140         if (!new_cache) {
141                 xtab_mount_read();
142                 exports_update(f_verbose);
143         }
144         xtab_export_write();
145         if (new_cache)
146                 cache_flush(force_flush);
147         if (!new_cache)
148                 xtab_mount_write();
149
150         return export_errno;
151 }
152
153 static void
154 exports_update_one(nfs_export *exp, int verbose)
155 {
156                 /* check mountpoint option */
157         if (exp->m_mayexport &&
158             exp->m_export.e_mountpoint &&
159             !is_mountpoint(exp->m_export.e_mountpoint[0]?
160                            exp->m_export.e_mountpoint:
161                            exp->m_export.e_path)) {
162                 printf("%s not exported as %s not a mountpoint.\n",
163                        exp->m_export.e_path, exp->m_export.e_mountpoint);
164                 exp->m_mayexport = 0;
165         }
166         if (exp->m_mayexport && ((exp->m_exported<1) || exp->m_changed)) {
167                 if (verbose)
168                         printf("%sexporting %s:%s to kernel\n",
169                                exp->m_exported ?"re":"",
170                                exp->m_client->m_hostname,
171                                exp->m_export.e_path);
172                 if (!export_export(exp))
173                         error(exp, errno);
174         }
175         if (exp->m_exported && ! exp->m_mayexport) {
176                 if (verbose)
177                         printf("unexporting %s:%s from kernel\n",
178                                exp->m_client->m_hostname,
179                                exp->m_export.e_path);
180                 if (!export_unexport(exp))
181                         error(exp, errno);
182         }
183 }
184
185
186 /* we synchronise intention with reality.
187  * entries with m_mayexport get exported
188  * entries with m_exported but not m_mayexport get unexported
189  * looking at m_client->m_type == MCL_FQDN and m_client->m_type == MCL_GSS only
190  */
191 static void
192 exports_update(int verbose)
193 {
194         nfs_export      *exp;
195
196         for (exp = exportlist[MCL_FQDN].p_head; exp; exp=exp->m_next) {
197                 exports_update_one(exp, verbose);
198         }
199         for (exp = exportlist[MCL_GSS].p_head; exp; exp=exp->m_next) {
200                 exports_update_one(exp, verbose);
201         }
202 }
203                         
204 /*
205  * export_all finds all entries and
206  *    marks them xtabent and mayexport so that they get exported
207  */
208 static void
209 export_all(int verbose)
210 {
211         nfs_export      *exp;
212         int             i;
213
214         for (i = 0; i < MCL_MAXTYPES; i++) {
215                 for (exp = exportlist[i].p_head; exp; exp = exp->m_next) {
216                         if (verbose)
217                                 printf("exporting %s:%s\n",
218                                        exp->m_client->m_hostname, 
219                                        exp->m_export.e_path);
220                         exp->m_xtabent = 1;
221                         exp->m_mayexport = 1;
222                         exp->m_changed = 1;
223                         exp->m_warned = 0;
224                         validate_export(exp);
225                 }
226         }
227 }
228
229
230 static void
231 exportfs(char *arg, char *options, int verbose)
232 {
233         struct exportent *eep;
234         nfs_export      *exp;
235         struct addrinfo *ai = NULL;
236         char            *path;
237         char            *hname = arg;
238         int             htype;
239
240         if ((path = strchr(arg, ':')) != NULL)
241                 *path++ = '\0';
242
243         if (!path || *path != '/') {
244                 fprintf(stderr, "Invalid exporting option: %s\n", arg);
245                 return;
246         }
247
248         if ((htype = client_gettype(hname)) == MCL_FQDN) {
249                 ai = host_addrinfo(hname);
250                 if (ai != NULL) {
251                         exp = export_find(ai, path);
252                         hname = ai->ai_canonname;
253                 }
254         } else
255                 exp = export_lookup(hname, path, 0);
256
257         if (!exp) {
258                 if (!(eep = mkexportent(hname, path, options)) ||
259                     !(exp = export_create(eep, 0)))
260                         goto out;
261         } else if (!updateexportent(&exp->m_export, options))
262                 goto out;
263
264         if (verbose)
265                 printf("exporting %s:%s\n", exp->m_client->m_hostname, 
266                         exp->m_export.e_path);
267         exp->m_xtabent = 1;
268         exp->m_mayexport = 1;
269         exp->m_changed = 1;
270         exp->m_warned = 0;
271         validate_export(exp);
272
273 out:
274         freeaddrinfo(ai);
275 }
276
277 static void
278 unexportfs(char *arg, int verbose)
279 {
280         nfs_export      *exp;
281         struct addrinfo *ai = NULL;
282         char            *path;
283         char            *hname = arg;
284         int             htype;
285
286         if ((path = strchr(arg, ':')) != NULL)
287                 *path++ = '\0';
288
289         if (!path || *path != '/') {
290                 fprintf(stderr, "Invalid unexporting option: %s\n",
291                         arg);
292                 return;
293         }
294
295         if ((htype = client_gettype(hname)) == MCL_FQDN) {
296                 ai = host_addrinfo(hname);
297                 if (ai)
298                         hname = ai->ai_canonname;
299         }
300
301         for (exp = exportlist[htype].p_head; exp; exp = exp->m_next) {
302                 if (path && strcmp(path, exp->m_export.e_path))
303                         continue;
304                 if (htype != exp->m_client->m_type)
305                         continue;
306                 if (htype == MCL_FQDN
307                     && !matchhostname(exp->m_export.e_hostname,
308                                           hname))
309                         continue;
310                 if (htype != MCL_FQDN
311                     && strcasecmp(exp->m_export.e_hostname, hname))
312                         continue;
313                 if (verbose) {
314 #if 0
315                         if (exp->m_exported) {
316                                 printf("unexporting %s:%s from kernel\n",
317                                        exp->m_client->m_hostname,
318                                        exp->m_export.e_path);
319                         }
320                         else
321 #endif
322                                 printf("unexporting %s:%s\n",
323                                         exp->m_client->m_hostname, 
324                                         exp->m_export.e_path);
325                 }
326 #if 0
327                 if (exp->m_exported && !export_unexport(exp))
328                         error(exp, errno);
329 #endif
330                 exp->m_xtabent = 0;
331                 exp->m_mayexport = 0;
332         }
333
334         freeaddrinfo(ai);
335 }
336
337 static int can_test(void)
338 {
339         int fd;
340         int n;
341         char *setup = "nfsd 0.0.0.0 2147483647 -test-client-\n";
342         fd = open("/proc/net/rpc/auth.unix.ip/channel", O_WRONLY);
343         if ( fd < 0) return 0;
344         n = write(fd, setup, strlen(setup));
345         close(fd);
346         if (n < 0)
347                 return 0;
348         fd = open("/proc/net/rpc/nfsd.export/channel", O_WRONLY);
349         if ( fd < 0) return 0;
350         close(fd);
351         return 1;
352 }
353
354 static int test_export(char *path, int with_fsid)
355 {
356         char buf[1024];
357         int fd, n;
358
359         sprintf(buf, "-test-client- %s 3 %d -1 -1 0\n",
360                 path,
361                 with_fsid ? NFSEXP_FSID : 0);
362         fd = open("/proc/net/rpc/nfsd.export/channel", O_WRONLY);
363         if (fd < 0)
364                 return 0;
365         n = write(fd, buf, strlen(buf));
366         close(fd);
367         if (n < 0)
368                 return 0;
369         return 1;
370 }
371
372 static void
373 validate_export(nfs_export *exp)
374 {
375         /* Check that the given export point is potentially exportable.
376          * We just give warnings here, don't cause anything to fail.
377          * If a path doesn't exist, or is not a dir or file, give an warning
378          * otherwise trial-export to '-test-client-' and check for failure.
379          */
380         struct stat stb;
381         char *path = exp->m_export.e_path;
382         struct statfs64 stf;
383         int fs_has_fsid = 0;
384
385         if (stat(path, &stb) < 0) {
386                 fprintf(stderr, "exportfs: Warning: %s does not exist\n",
387                         path);
388                 return;
389         }
390         if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) {
391                 fprintf(stderr, "exportfs: Warning: %s is neither "
392                         "a directory nor a file.\n"
393                         "     remote access will fail\n", path);
394                 return;
395         }
396         if (!can_test())
397                 return;
398
399         if (!statfs64(path, &stf) &&
400             (stf.f_fsid.__val[0] || stf.f_fsid.__val[1]))
401                 fs_has_fsid = 1;
402
403         if ((exp->m_export.e_flags & NFSEXP_FSID) || exp->m_export.e_uuid ||
404             fs_has_fsid) {
405                 if ( !test_export(path, 1)) {
406                         fprintf(stderr, "exportfs: Warning: %s does not "
407                                 "support NFS export.\n",
408                                 path);
409                         return;
410                 }
411         } else if ( ! test_export(path, 0)) {
412                 if (test_export(path, 1))
413                         fprintf(stderr, "exportfs: Warning: %s requires fsid= "
414                                 "for NFS export\n", path);
415                 else
416                         fprintf(stderr, "exportfs: Warning: %s does not "
417                                 "support NFS export.\n",
418                                 path);
419                 return;
420
421         }
422 }
423
424
425 static char
426 dumpopt(char c, char *fmt, ...)
427 {
428         va_list ap;
429
430         va_start(ap, fmt);
431         printf("%c", c);
432         vprintf(fmt, ap);
433         va_end(ap);
434         return ',';
435 }
436
437 static void
438 dump(int verbose)
439 {
440         nfs_export      *exp;
441         struct exportent *ep;
442         int             htype;
443         char            *hname, c;
444
445         for (htype = 0; htype < MCL_MAXTYPES; htype++) {
446                 for (exp = exportlist[htype].p_head; exp; exp = exp->m_next) {
447                         ep = &exp->m_export;
448                         if (!exp->m_xtabent)
449                             continue; /* neilb */
450                         if (htype == MCL_ANONYMOUS)
451                                 hname = "<world>";
452                         else
453                                 hname = ep->e_hostname;
454                         if (strlen(ep->e_path) > 14)
455                                 printf("%-14s\n\t\t%s", ep->e_path, hname);
456                         else
457                                 printf("%-14s\t%s", ep->e_path, hname);
458                         if (!verbose) {
459                                 printf("\n");
460                                 continue;
461                         }
462                         c = '(';
463                         if (ep->e_flags & NFSEXP_READONLY)
464                                 c = dumpopt(c, "ro");
465                         else
466                                 c = dumpopt(c, "rw");
467                         if (ep->e_flags & NFSEXP_ASYNC)
468                                 c = dumpopt(c, "async");
469                         if (ep->e_flags & NFSEXP_GATHERED_WRITES)
470                                 c = dumpopt(c, "wdelay");
471                         if (ep->e_flags & NFSEXP_NOHIDE)
472                                 c = dumpopt(c, "nohide");
473                         if (ep->e_flags & NFSEXP_CROSSMOUNT)
474                                 c = dumpopt(c, "crossmnt");
475                         if (ep->e_flags & NFSEXP_INSECURE_PORT)
476                                 c = dumpopt(c, "insecure");
477                         if (ep->e_flags & NFSEXP_ROOTSQUASH)
478                                 c = dumpopt(c, "root_squash");
479                         else
480                                 c = dumpopt(c, "no_root_squash");
481                         if (ep->e_flags & NFSEXP_ALLSQUASH)
482                                 c = dumpopt(c, "all_squash");
483                         if (ep->e_flags & NFSEXP_NOSUBTREECHECK)
484                                 c = dumpopt(c, "no_subtree_check");
485                         if (ep->e_flags & NFSEXP_NOAUTHNLM)
486                                 c = dumpopt(c, "insecure_locks");
487                         if (ep->e_flags & NFSEXP_NOACL)
488                                 c = dumpopt(c, "no_acl");
489                         if (ep->e_flags & NFSEXP_FSID)
490                                 c = dumpopt(c, "fsid=%d", ep->e_fsid);
491                         if (ep->e_uuid)
492                                 c = dumpopt(c, "fsid=%s", ep->e_uuid);
493                         if (ep->e_mountpoint)
494                                 c = dumpopt(c, "mountpoint%s%s", 
495                                             ep->e_mountpoint[0]?"=":"", 
496                                             ep->e_mountpoint);
497                         if (ep->e_anonuid != 65534)
498                                 c = dumpopt(c, "anonuid=%d", ep->e_anonuid);
499                         if (ep->e_anongid != 65534)
500                                 c = dumpopt(c, "anongid=%d", ep->e_anongid);
501                         switch(ep->e_fslocmethod) {
502                         case FSLOC_NONE:
503                                 break;
504                         case FSLOC_REFER:
505                                 c = dumpopt(c, "refer=%s", ep->e_fslocdata);
506                                 break;
507                         case FSLOC_REPLICA:
508                                 c = dumpopt(c, "replicas=%s", ep->e_fslocdata);
509                                 break;
510 #ifdef DEBUG
511                         case FSLOC_STUB:
512                                 c = dumpopt(c, "fsloc=stub");
513                                 break;
514 #endif
515                         }
516                         secinfo_show(stdout, ep);
517                         printf("%c\n", (c != '(')? ')' : ' ');
518                 }
519         }
520 }
521
522 static void
523 error(nfs_export *exp, int err)
524 {
525         fprintf(stderr, "%s:%s: %s\n", exp->m_client->m_hostname, 
526                 exp->m_export.e_path, strerror(err));
527 }
528
529 static void
530 usage(void)
531 {
532         fprintf(stderr, "usage: exportfs [-aruv] [host:/path]\n");
533         exit(1);
534 }