exportfs: Add a common exit label in exportfs()
[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 hostent  *hp = 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             (hp = gethostbyname(hname)) != NULL) {
250                 struct hostent *hp2 = hostent_dup (hp);
251                 hp = gethostbyaddr(hp2->h_addr, hp2->h_length,
252                                    hp2->h_addrtype);
253                 if (hp) {
254                         free(hp2);
255                         hp = hostent_dup(hp);
256                 } else
257                         hp = hp2;
258                 exp = export_find(hp, path);
259                 hname = hp->h_name;
260         } else {
261                 exp = export_lookup(hname, path, 0);
262         }
263
264         if (!exp) {
265                 if (!(eep = mkexportent(hname, path, options)) ||
266                     !(exp = export_create(eep, 0)))
267                         goto out;
268         } else if (!updateexportent(&exp->m_export, options))
269                 goto out;
270
271         if (verbose)
272                 printf("exporting %s:%s\n", exp->m_client->m_hostname, 
273                         exp->m_export.e_path);
274         exp->m_xtabent = 1;
275         exp->m_mayexport = 1;
276         exp->m_changed = 1;
277         exp->m_warned = 0;
278         validate_export(exp);
279
280 out:
281         if (hp) free (hp);
282 }
283
284 static void
285 unexportfs(char *arg, int verbose)
286 {
287         nfs_export      *exp;
288         struct addrinfo *ai = NULL;
289         char            *path;
290         char            *hname = arg;
291         int             htype;
292
293         if ((path = strchr(arg, ':')) != NULL)
294                 *path++ = '\0';
295
296         if (!path || *path != '/') {
297                 fprintf(stderr, "Invalid unexporting option: %s\n",
298                         arg);
299                 return;
300         }
301
302         if ((htype = client_gettype(hname)) == MCL_FQDN) {
303                 ai = host_addrinfo(hname);
304                 if (ai)
305                         hname = ai->ai_canonname;
306         }
307
308         for (exp = exportlist[htype].p_head; exp; exp = exp->m_next) {
309                 if (path && strcmp(path, exp->m_export.e_path))
310                         continue;
311                 if (htype != exp->m_client->m_type)
312                         continue;
313                 if (htype == MCL_FQDN
314                     && !matchhostname(exp->m_export.e_hostname,
315                                           hname))
316                         continue;
317                 if (htype != MCL_FQDN
318                     && strcasecmp(exp->m_export.e_hostname, hname))
319                         continue;
320                 if (verbose) {
321 #if 0
322                         if (exp->m_exported) {
323                                 printf("unexporting %s:%s from kernel\n",
324                                        exp->m_client->m_hostname,
325                                        exp->m_export.e_path);
326                         }
327                         else
328 #endif
329                                 printf("unexporting %s:%s\n",
330                                         exp->m_client->m_hostname, 
331                                         exp->m_export.e_path);
332                 }
333 #if 0
334                 if (exp->m_exported && !export_unexport(exp))
335                         error(exp, errno);
336 #endif
337                 exp->m_xtabent = 0;
338                 exp->m_mayexport = 0;
339         }
340
341         freeaddrinfo(ai);
342 }
343
344 static int can_test(void)
345 {
346         int fd;
347         int n;
348         char *setup = "nfsd 0.0.0.0 2147483647 -test-client-\n";
349         fd = open("/proc/net/rpc/auth.unix.ip/channel", O_WRONLY);
350         if ( fd < 0) return 0;
351         n = write(fd, setup, strlen(setup));
352         close(fd);
353         if (n < 0)
354                 return 0;
355         fd = open("/proc/net/rpc/nfsd.export/channel", O_WRONLY);
356         if ( fd < 0) return 0;
357         close(fd);
358         return 1;
359 }
360
361 static int test_export(char *path, int with_fsid)
362 {
363         char buf[1024];
364         int fd, n;
365
366         sprintf(buf, "-test-client- %s 3 %d -1 -1 0\n",
367                 path,
368                 with_fsid ? NFSEXP_FSID : 0);
369         fd = open("/proc/net/rpc/nfsd.export/channel", O_WRONLY);
370         if (fd < 0)
371                 return 0;
372         n = write(fd, buf, strlen(buf));
373         close(fd);
374         if (n < 0)
375                 return 0;
376         return 1;
377 }
378
379 static void
380 validate_export(nfs_export *exp)
381 {
382         /* Check that the given export point is potentially exportable.
383          * We just give warnings here, don't cause anything to fail.
384          * If a path doesn't exist, or is not a dir or file, give an warning
385          * otherwise trial-export to '-test-client-' and check for failure.
386          */
387         struct stat stb;
388         char *path = exp->m_export.e_path;
389         struct statfs64 stf;
390         int fs_has_fsid = 0;
391
392         if (stat(path, &stb) < 0) {
393                 fprintf(stderr, "exportfs: Warning: %s does not exist\n",
394                         path);
395                 return;
396         }
397         if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) {
398                 fprintf(stderr, "exportfs: Warning: %s is neither "
399                         "a directory nor a file.\n"
400                         "     remote access will fail\n", path);
401                 return;
402         }
403         if (!can_test())
404                 return;
405
406         if (!statfs64(path, &stf) &&
407             (stf.f_fsid.__val[0] || stf.f_fsid.__val[1]))
408                 fs_has_fsid = 1;
409
410         if ((exp->m_export.e_flags & NFSEXP_FSID) || exp->m_export.e_uuid ||
411             fs_has_fsid) {
412                 if ( !test_export(path, 1)) {
413                         fprintf(stderr, "exportfs: Warning: %s does not "
414                                 "support NFS export.\n",
415                                 path);
416                         return;
417                 }
418         } else if ( ! test_export(path, 0)) {
419                 if (test_export(path, 1))
420                         fprintf(stderr, "exportfs: Warning: %s requires fsid= "
421                                 "for NFS export\n", path);
422                 else
423                         fprintf(stderr, "exportfs: Warning: %s does not "
424                                 "support NFS export.\n",
425                                 path);
426                 return;
427
428         }
429 }
430
431
432 static char
433 dumpopt(char c, char *fmt, ...)
434 {
435         va_list ap;
436
437         va_start(ap, fmt);
438         printf("%c", c);
439         vprintf(fmt, ap);
440         va_end(ap);
441         return ',';
442 }
443
444 static void
445 dump(int verbose)
446 {
447         nfs_export      *exp;
448         struct exportent *ep;
449         int             htype;
450         char            *hname, c;
451
452         for (htype = 0; htype < MCL_MAXTYPES; htype++) {
453                 for (exp = exportlist[htype].p_head; exp; exp = exp->m_next) {
454                         ep = &exp->m_export;
455                         if (!exp->m_xtabent)
456                             continue; /* neilb */
457                         if (htype == MCL_ANONYMOUS)
458                                 hname = "<world>";
459                         else
460                                 hname = ep->e_hostname;
461                         if (strlen(ep->e_path) > 14)
462                                 printf("%-14s\n\t\t%s", ep->e_path, hname);
463                         else
464                                 printf("%-14s\t%s", ep->e_path, hname);
465                         if (!verbose) {
466                                 printf("\n");
467                                 continue;
468                         }
469                         c = '(';
470                         if (ep->e_flags & NFSEXP_READONLY)
471                                 c = dumpopt(c, "ro");
472                         else
473                                 c = dumpopt(c, "rw");
474                         if (ep->e_flags & NFSEXP_ASYNC)
475                                 c = dumpopt(c, "async");
476                         if (ep->e_flags & NFSEXP_GATHERED_WRITES)
477                                 c = dumpopt(c, "wdelay");
478                         if (ep->e_flags & NFSEXP_NOHIDE)
479                                 c = dumpopt(c, "nohide");
480                         if (ep->e_flags & NFSEXP_CROSSMOUNT)
481                                 c = dumpopt(c, "crossmnt");
482                         if (ep->e_flags & NFSEXP_INSECURE_PORT)
483                                 c = dumpopt(c, "insecure");
484                         if (ep->e_flags & NFSEXP_ROOTSQUASH)
485                                 c = dumpopt(c, "root_squash");
486                         else
487                                 c = dumpopt(c, "no_root_squash");
488                         if (ep->e_flags & NFSEXP_ALLSQUASH)
489                                 c = dumpopt(c, "all_squash");
490                         if (ep->e_flags & NFSEXP_NOSUBTREECHECK)
491                                 c = dumpopt(c, "no_subtree_check");
492                         if (ep->e_flags & NFSEXP_NOAUTHNLM)
493                                 c = dumpopt(c, "insecure_locks");
494                         if (ep->e_flags & NFSEXP_NOACL)
495                                 c = dumpopt(c, "no_acl");
496                         if (ep->e_flags & NFSEXP_FSID)
497                                 c = dumpopt(c, "fsid=%d", ep->e_fsid);
498                         if (ep->e_uuid)
499                                 c = dumpopt(c, "fsid=%s", ep->e_uuid);
500                         if (ep->e_mountpoint)
501                                 c = dumpopt(c, "mountpoint%s%s", 
502                                             ep->e_mountpoint[0]?"=":"", 
503                                             ep->e_mountpoint);
504                         if (ep->e_anonuid != 65534)
505                                 c = dumpopt(c, "anonuid=%d", ep->e_anonuid);
506                         if (ep->e_anongid != 65534)
507                                 c = dumpopt(c, "anongid=%d", ep->e_anongid);
508                         switch(ep->e_fslocmethod) {
509                         case FSLOC_NONE:
510                                 break;
511                         case FSLOC_REFER:
512                                 c = dumpopt(c, "refer=%s", ep->e_fslocdata);
513                                 break;
514                         case FSLOC_REPLICA:
515                                 c = dumpopt(c, "replicas=%s", ep->e_fslocdata);
516                                 break;
517 #ifdef DEBUG
518                         case FSLOC_STUB:
519                                 c = dumpopt(c, "fsloc=stub");
520                                 break;
521 #endif
522                         }
523                         secinfo_show(stdout, ep);
524                         printf("%c\n", (c != '(')? ')' : ' ');
525                 }
526         }
527 }
528
529 static void
530 error(nfs_export *exp, int err)
531 {
532         fprintf(stderr, "%s:%s: %s\n", exp->m_client->m_hostname, 
533                 exp->m_export.e_path, strerror(err));
534 }
535
536 static void
537 usage(void)
538 {
539         fprintf(stderr, "usage: exportfs [-aruv] [host:/path]\n");
540         exit(1);
541 }