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