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