593a8ebd467e1081b4f7ed4110df89712bc8c248
[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 <unistd.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <stdarg.h>
20 #include <getopt.h>
21 #include <netdb.h>
22 #include <errno.h>
23 #include "xmalloc.h"
24 #include "misc.h"
25 #include "nfslib.h"
26 #include "exportfs.h"
27 #include "xmalloc.h"
28 #include "xlog.h"
29
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);
38
39 int
40 main(int argc, char **argv)
41 {
42         char    *options = NULL;
43         int     f_export = 1;
44         int     f_all = 0;
45         int     f_verbose = 0;
46         int     f_reexport = 0;
47         int     f_ignore = 0;
48         int     i, c;
49         int     new_cache = 0;
50         int     force_flush = 0;
51
52         xlog_open("exportfs");
53
54         export_errno = 0;
55
56         while ((c = getopt(argc, argv, "aio:ruvf")) != EOF) {
57                 switch(c) {
58                 case 'a':
59                         f_all = 1;
60                         break;
61                 case 'i':
62                         f_ignore = 1;
63                         break;
64                 case 'o':
65                         options = optarg;
66                         break;
67                 case 'r':
68                         f_reexport = 1;
69                         f_all = 1;
70                         break;
71                 case 'u':
72                         f_export = 0;
73                         break;
74                 case 'v':
75                         f_verbose = 1;
76                         break;
77                 case 'f':
78                         force_flush = 1;
79                         break;
80                 default:
81                         usage();
82                         break;
83                 }
84         }
85
86         if (optind != argc && f_all) {
87                 fprintf(stderr,"exportfs: extra arguments are not permitted with -a or -r.\n");
88                 return 1;
89         }
90         if (f_ignore && (f_all || ! f_export)) {
91                 fprintf(stderr,"exportfs: -i not meaningful with -a, -r or -u.\n");
92                 return 1;
93         }
94         if (f_reexport && ! f_export) {
95                 fprintf(stderr, "exportfs: -r and -u are incompatible.\n");
96                 return 1;
97         }
98         new_cache = check_new_cache();
99         if (optind == argc && ! f_all) {
100                 if (force_flush) {
101                         if (new_cache)
102                                 cache_flush(1);
103                         else {
104                                 fprintf(stderr, "exportfs: -f: only available with new cache controls: mount /proc/fs/nfsd first\n");
105                                 exit(1);
106                         }
107                         return 0;
108                 } else {
109                         xtab_export_read();
110                         dump(f_verbose);
111                         return 0;
112                 }
113         }
114         if (f_export && ! f_ignore)
115                 export_read(_PATH_EXPORTS);
116         if (f_export) {
117                 if (f_all)
118                         export_all(f_verbose);
119                 else
120                         for (i = optind; i < argc ; i++)
121                                 exportfs(argv[i], options, f_verbose);
122         }
123         /* If we are unexporting everything, then
124          * don't care about what should be exported, as that
125          * may require DNS lookups..
126          */
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
130                  */
131                 if (!f_reexport)
132                         xtab_export_read();
133                 if (!f_export)
134                         for (i = optind ; i < argc ; i++)
135                                 unexportfs(argv[i], f_verbose);
136                 if (!new_cache)
137                         rmtab_read();
138         }
139         if (!new_cache) {
140                 xtab_mount_read();
141                 exports_update(f_verbose);
142         }
143         xtab_export_write();
144         if (new_cache)
145                 cache_flush(force_flush);
146         if (!new_cache)
147                 xtab_mount_write();
148
149         return export_errno;
150 }
151
152 static void
153 exports_update_one(nfs_export *exp, int verbose)
154 {
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;
164         }
165         if (exp->m_mayexport && ((exp->m_exported<1) || exp->m_changed)) {
166                 if (verbose)
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))
172                         error(exp, errno);
173         }
174         if (exp->m_exported && ! exp->m_mayexport) {
175                 if (verbose)
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))
180                         error(exp, errno);
181         }
182 }
183
184
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
189  */
190 static void
191 exports_update(int verbose)
192 {
193         nfs_export      *exp;
194
195         for (exp = exportlist[MCL_FQDN].p_head; exp; exp=exp->m_next) {
196                 exports_update_one(exp, verbose);
197         }
198         for (exp = exportlist[MCL_GSS].p_head; exp; exp=exp->m_next) {
199                 exports_update_one(exp, verbose);
200         }
201 }
202                         
203 /*
204  * export_all finds all entries and
205  *    marks them xtabent and mayexport so that they get exported
206  */
207 static void
208 export_all(int verbose)
209 {
210         nfs_export      *exp;
211         int             i;
212
213         for (i = 0; i < MCL_MAXTYPES; i++) {
214                 for (exp = exportlist[i].p_head; exp; exp = exp->m_next) {
215                         if (verbose)
216                                 printf("exporting %s:%s\n",
217                                        exp->m_client->m_hostname, 
218                                        exp->m_export.e_path);
219                         exp->m_xtabent = 1;
220                         exp->m_mayexport = 1;
221                         exp->m_changed = 1;
222                         exp->m_warned = 0;
223                         validate_export(exp);
224                 }
225         }
226 }
227
228
229 static void
230 exportfs(char *arg, char *options, int verbose)
231 {
232         struct exportent *eep;
233         nfs_export      *exp;
234         struct hostent  *hp = NULL;
235         char            *path;
236         char            *hname = arg;
237         int             htype;
238
239         if ((path = strchr(arg, ':')) != NULL)
240                 *path++ = '\0';
241
242         if (!path || *path != '/') {
243                 fprintf(stderr, "Invalid exporting option: %s\n", arg);
244                 return;
245         }
246
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,
251                                    hp2->h_addrtype);
252                 if (hp) {
253                         free(hp2);
254                         hp = hostent_dup(hp);
255                 } else
256                         hp = hp2;
257                 exp = export_find(hp, path);
258                 hname = hp->h_name;
259         } else {
260                 exp = export_lookup(hname, path, 0);
261         }
262
263         if (!exp) {
264                 if (!(eep = mkexportent(hname, path, options)) ||
265                     !(exp = export_create(eep, 0))) {
266                         if (hp) free (hp);
267                         return;
268                 }
269         } else if (!updateexportent(&exp->m_export, options)) {
270                 if (hp) free (hp);
271                 return;
272         }
273
274         if (verbose)
275                 printf("exporting %s:%s\n", exp->m_client->m_hostname, 
276                         exp->m_export.e_path);
277         exp->m_xtabent = 1;
278         exp->m_mayexport = 1;
279         exp->m_changed = 1;
280         exp->m_warned = 0;
281         validate_export(exp);
282         if (hp) free (hp);
283 }
284
285 static void
286 unexportfs(char *arg, int verbose)
287 {
288         nfs_export      *exp;
289         struct hostent  *hp = NULL;
290         char            *path;
291         char            *hname = arg;
292         int             htype;
293
294         if ((path = strchr(arg, ':')) != NULL)
295                 *path++ = '\0';
296
297         if (!path || *path != '/') {
298                 fprintf(stderr, "Invalid unexporting option: %s\n",
299                         arg);
300                 return;
301         }
302
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;
307                 }
308         }
309
310         for (exp = exportlist[htype].p_head; exp; exp = exp->m_next) {
311                 if (path && strcmp(path, exp->m_export.e_path))
312                         continue;
313                 if (htype != exp->m_client->m_type)
314                         continue;
315                 if (htype == MCL_FQDN
316                     && !matchhostname(exp->m_export.e_hostname,
317                                           hname))
318                         continue;
319                 if (htype != MCL_FQDN
320                     && strcasecmp(exp->m_export.e_hostname, hname))
321                         continue;
322                 if (verbose) {
323 #if 0
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);
328                         }
329                         else
330 #endif
331                                 printf("unexporting %s:%s\n",
332                                         exp->m_client->m_hostname, 
333                                         exp->m_export.e_path);
334                 }
335 #if 0
336                 if (exp->m_exported && !export_unexport(exp))
337                         error(exp, errno);
338 #endif
339                 exp->m_xtabent = 0;
340                 exp->m_mayexport = 0;
341         }
342
343         if (hp) free (hp);
344 }
345
346 static int can_test(void)
347 {
348         int fd;
349         int n;
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));
354         close(fd);
355         if (n < 0)
356                 return 0;
357         fd = open("/proc/net/rpc/nfsd.export/channel", O_WRONLY);
358         if ( fd < 0) return 0;
359         close(fd);
360         return 1;
361 }
362
363 static int test_export(char *path, int with_fsid)
364 {
365         char buf[1024];
366         int fd, n;
367
368         sprintf(buf, "-test-client- %s 3 %d -1 -1 0\n",
369                 path,
370                 with_fsid ? NFSEXP_FSID : 0);
371         fd = open("/proc/net/rpc/nfsd.export/channel", O_WRONLY);
372         if (fd < 0)
373                 return 0;
374         n = write(fd, buf, strlen(buf));
375         close(fd);
376         if (n < 0)
377                 return 0;
378         return 1;
379 }
380
381 static void
382 validate_export(nfs_export *exp)
383 {
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.
388          */
389         struct stat stb;
390         char *path = exp->m_export.e_path;
391         struct statfs64 stf;
392         int fs_has_fsid = 0;
393
394         if (stat(path, &stb) < 0) {
395                 fprintf(stderr, "exportfs: Warning: %s does not exist\n",
396                         path);
397                 return;
398         }
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);
403                 return;
404         }
405         if (!can_test())
406                 return;
407
408         if (!statfs64(path, &stf) &&
409             (stf.f_fsid.__val[0] || stf.f_fsid.__val[1]))
410                 fs_has_fsid = 1;
411
412         if ((exp->m_export.e_flags & NFSEXP_FSID) || exp->m_export.e_uuid ||
413             fs_has_fsid) {
414                 if ( !test_export(path, 1)) {
415                         fprintf(stderr, "exportfs: Warning: %s does not "
416                                 "support NFS export.\n",
417                                 path);
418                         return;
419                 }
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);
424                 else
425                         fprintf(stderr, "exportfs: Warning: %s does not "
426                                 "support NFS export.\n",
427                                 path);
428                 return;
429
430         }
431 }
432
433
434 static char
435 dumpopt(char c, char *fmt, ...)
436 {
437         va_list ap;
438
439         va_start(ap, fmt);
440         printf("%c", c);
441         vprintf(fmt, ap);
442         va_end(ap);
443         return ',';
444 }
445
446 static void
447 dump(int verbose)
448 {
449         nfs_export      *exp;
450         struct exportent *ep;
451         int             htype;
452         char            *hname, c;
453
454         for (htype = 0; htype < MCL_MAXTYPES; htype++) {
455                 for (exp = exportlist[htype].p_head; exp; exp = exp->m_next) {
456                         ep = &exp->m_export;
457                         if (!exp->m_xtabent)
458                             continue; /* neilb */
459                         if (htype == MCL_ANONYMOUS)
460                                 hname = "<world>";
461                         else
462                                 hname = ep->e_hostname;
463                         if (strlen(ep->e_path) > 14)
464                                 printf("%-14s\n\t\t%s", ep->e_path, hname);
465                         else
466                                 printf("%-14s\t%s", ep->e_path, hname);
467                         if (!verbose) {
468                                 printf("\n");
469                                 continue;
470                         }
471                         c = '(';
472                         if (ep->e_flags & NFSEXP_READONLY)
473                                 c = dumpopt(c, "ro");
474                         else
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");
488                         else
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);
500                         if (ep->e_uuid)
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]?"=":"", 
505                                             ep->e_mountpoint);
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) {
511                         case FSLOC_NONE:
512                                 break;
513                         case FSLOC_REFER:
514                                 c = dumpopt(c, "refer=%s", ep->e_fslocdata);
515                                 break;
516                         case FSLOC_REPLICA:
517                                 c = dumpopt(c, "replicas=%s", ep->e_fslocdata);
518                                 break;
519 #ifdef DEBUG
520                         case FSLOC_STUB:
521                                 c = dumpopt(c, "fsloc=stub");
522                                 break;
523 #endif
524                         }
525                         secinfo_show(stdout, ep);
526                         printf("%c\n", (c != '(')? ')' : ' ');
527                 }
528         }
529 }
530
531 static void
532 error(nfs_export *exp, int err)
533 {
534         fprintf(stderr, "%s:%s: %s\n", exp->m_client->m_hostname, 
535                 exp->m_export.e_path, strerror(err));
536 }
537
538 static void
539 usage(void)
540 {
541         fprintf(stderr, "usage: exportfs [-aruv] [host:/path]\n");
542         exit(1);
543 }