Added initial unfs3 sources for version 0.9.22+dfsg-1maemo2
[unfs3] / unfs3 / Config / exports.y
1 %{
2 /*
3  * UNFS3 exports parser and export controls
4  * (C) 2004, Pascal Schmidt
5  * see file LICENSE for license details
6  */
7 #include "../config.h"
8
9 #include <rpc/rpc.h>
10 #include <limits.h>
11
12 #ifdef WIN32
13 #include "../winsupport.h"
14 #else
15 #include <netdb.h>
16 #include <syslog.h>
17 #include <unistd.h>
18 #include <netinet/in.h>
19 #include <arpa/inet.h>
20 #endif /* WIN32 */
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include "../nfs.h"
26 #include "../mount.h"
27 #include "../daemon.h"
28 #include "../backend.h"
29 #include "exports.h"
30
31 #ifndef PATH_MAX
32 # define PATH_MAX       4096
33 #endif
34
35 /* for lack of a better place */
36 #ifdef __GNUC__
37 #define U(x) x __attribute__ ((unused))
38 #else
39 #define U(x) x
40 #endif
41
42 /* lexer stuff, to avoid compiler warnings */
43 int yylex(void);
44 extern FILE *yyin;
45
46 /*
47  * C code used by yacc parser
48  */
49
50 typedef struct {
51         char            orig[NFS_MAXPATHLEN];
52         int             options;
53         char            password[PASSWORD_MAXLEN+1];
54         uint32          password_hash;
55         struct in_addr  addr;
56         struct in_addr  mask;
57         uint32          anonuid;
58         uint32          anongid;
59         struct e_host   *next;
60 } e_host;
61
62 typedef struct {
63         char            path[NFS_MAXPATHLEN];
64         char            orig[NFS_MAXPATHLEN];
65         e_host          *hosts;
66         uint32          fsid; /* export point fsid (for removables) */
67         time_t          last_mtime; /* Last returned mtime (for removables) */
68         uint32          dir_hash; /* Hash of dir contents (for removables) */
69         struct e_item   *next;
70 } e_item;
71
72 /* export list, item, and host filled during parse */
73 static e_item *e_list = NULL;
74 static e_item cur_item;
75 static e_host cur_host;
76
77 /* last looked-up anonuid and anongid */
78 static uint32 last_anonuid = ANON_NOTSPECIAL;
79 static uint32 last_anongid = ANON_NOTSPECIAL;
80
81 /* mount protocol compatible variants */
82 static exports ne_list = NULL;
83 static struct exportnode ne_item;
84 static struct groupnode ne_host;
85
86 /* error status of last parse */
87 int e_error = FALSE;
88
89 /*
90  * The FNV1a-32 hash algorithm
91  * (http://www.isthe.com/chongo/tech/comp/fnv/)
92  */
93 uint32 fnv1a_32(const char *str, uint32 hval)
94 {
95     static const uint32 fnv_32_prime = 0x01000193;
96     
97     while (*str) {
98         hval ^= *str++;
99         hval *= fnv_32_prime;
100     }
101     return hval;
102 }
103
104 #ifdef WIN32
105 uint32 wfnv1a_32(const wchar_t *str, uint32 hval)
106 {
107     static const uint32 fnv_32_prime = 0x01000193;
108     
109     while (*str) {
110         hval ^= *str++;
111         hval *= fnv_32_prime;
112     }
113     return hval;
114 }
115 #endif
116
117 /*
118  * get static fsid, for use with removable media export points
119  */
120 static uint32 get_free_fsid(const char *path)
121 {
122     uint32 hval;
123
124     /* The 32:th bit is set to one on all special filehandles. The
125        last 31 bits are hashed from the export point path. */
126     hval = fnv1a_32(path, 0);
127     hval |= 0x80000000;
128     return hval;
129 }
130
131
132 /*
133  * clear current host
134  */
135 static void clear_host(void)
136 {
137         memset(&cur_host, 0, sizeof(e_host));
138         strcpy(cur_host.orig, "<anon clnt>");
139         memset(&ne_host, 0, sizeof(struct groupnode));
140         
141         cur_host.anonuid =
142         cur_host.anongid = ANON_NOTSPECIAL; 
143 }
144
145 /*
146  * clear current item
147  */
148 static void clear_item(void)
149 {
150         memset(&cur_item, 0, sizeof(e_item));
151         memset(&ne_item, 0, sizeof(struct exportnode));
152 }
153
154 /*
155  * add current host to current export item
156  */
157 static void add_host(void)
158 {
159         e_host *new;
160         e_host *iter;
161         
162         groups ne_new;
163         groups ne_iter;
164
165         new = malloc(sizeof(e_host));
166         ne_new = malloc(sizeof(struct groupnode));
167         if (!new || !ne_new) {
168                 logmsg(LOG_EMERG, "out of memory, aborting");
169                 daemon_exit(CRISIS);
170         }
171
172         *new = cur_host;
173         *ne_new = ne_host;
174         ne_new->gr_name = new->orig;
175
176         /* internal list */
177         if (cur_item.hosts) {
178                 iter = cur_item.hosts;
179                 while (iter->next)
180                         iter = (e_host *) iter->next;
181                 iter->next = (struct e_host *) new;
182         } else
183                 cur_item.hosts = new;
184
185         /* matching mount protocol list */
186         if (ne_item.ex_groups) {
187                 ne_iter = ne_item.ex_groups;
188                 while (ne_iter->gr_next)
189                         ne_iter = (groups) ne_iter->gr_next;
190                 ne_iter->gr_next = ne_new;
191         } else
192                 ne_item.ex_groups = ne_new;
193         
194         clear_host();
195 }
196
197 /* 
198    Normalize path, eliminating double slashes, etc. To be used instead
199    of realpath, when realpath is not possible. Normalizing export
200    points is important. Otherwise, mount requests might fail, since
201    /x/y is not a prefix of ///x///y/ etc.
202 */
203 char *normpath(const char *path, char *normpath)
204 {
205         char *n;
206         const char *p;
207
208         /* Copy path to normpath, and replace blocks of slashes with
209            single slash */
210         p = path;
211         n = normpath;
212         while (*p) {
213                 /* Skip over multiple slashes */
214                 if (*p == '/' && *(p+1) == '/') {
215                         p++;
216                         continue;
217                 }
218                 *n++ = *p++;
219         }
220         *n = '\0';
221
222         /* Remove trailing slash, if any. */
223         if ((n - normpath) > 1 && *(n-1) == '/')
224                 *(n-1) = '\0';
225
226         return normpath;
227 }
228
229 /*
230  * add current item to current export list
231  */
232 static void add_item(const char *path)
233 {
234         char buf[PATH_MAX];
235         e_item *new;
236         e_item *iter;
237         e_host *host;
238         /* Is this item marked as removable for all hosts? */
239         int removable_for_all = 1;
240         
241         exports ne_new;
242         exports ne_iter;
243
244         new = malloc(sizeof(e_item));
245         ne_new = malloc(sizeof(struct exportnode));
246         if (!new || !ne_new) {
247                 logmsg(LOG_EMERG, "out of memory, aborting");
248                 daemon_exit(CRISIS);
249         }
250
251         /* Loop over all hosts and check if marked as removable. */
252         host = cur_item.hosts;
253         while (host) {
254                 if (!(host->options & OPT_REMOVABLE))
255                         removable_for_all = 0;
256                 host = (e_host *) host->next;
257         }
258
259         if (removable_for_all) {
260                 /* If marked as removable for all hosts, don't try
261                    realpath. */
262                 normpath(path, buf);
263         } else if (!backend_realpath(path, buf)) {
264                 logmsg(LOG_CRIT, "realpath for %s failed", path);
265                 e_error = TRUE;
266                 free(new);
267                 free(ne_new);
268                 clear_item();
269                 return;
270         }
271
272         if (strlen(buf) + 1 > NFS_MAXPATHLEN) {
273                 logmsg(LOG_CRIT, "attempted to export too long path");
274                 e_error = TRUE;
275                 free(new);
276                 free(ne_new);
277                 clear_item();
278                 return;
279         }
280
281         /* if no hosts listed, list default host */
282         if (!cur_item.hosts)
283                 add_host();
284
285         *new = cur_item;
286         strcpy(new->path, buf);
287         strcpy(new->orig, path);
288         new->fsid = get_free_fsid(path);  
289         new->last_mtime = 0;
290         new->dir_hash = 0;
291
292         *ne_new = ne_item;
293         ne_new->ex_dir = new->orig;
294
295         /* internal list */
296         if (e_list) {
297                 iter = e_list;
298                 while (iter->next)
299                         iter = (e_item *) iter->next;
300                 iter->next = (struct e_item *) new;
301         } else
302                 e_list = new;
303
304         /* matching mount protocol list */
305         if (ne_list) {
306                 ne_iter = ne_list;
307                 while (ne_iter->ex_next)
308                         ne_iter = (exports) ne_iter->ex_next;
309                 ne_iter->ex_next = ne_new;
310         } else
311                 ne_list = ne_new;
312                 
313         clear_item();
314 }
315
316 /*
317  * fill current host's address given a hostname
318  */
319 static void set_hostname(const char *name)
320 {
321         struct hostent *ent;
322
323         if (strlen(name) + 1 > NFS_MAXPATHLEN) {
324                 e_error = TRUE;
325                 return;
326         }
327         strcpy(cur_host.orig, name);
328
329         ent = gethostbyname(name);
330
331         if (ent) {
332                 memcpy(&cur_host.addr, ent->h_addr_list[0],
333                        sizeof(struct in_addr));
334                 cur_host.mask.s_addr = ~0UL;
335         } else {
336                 logmsg(LOG_CRIT, "could not resolve hostname '%s'", name);
337                 e_error = TRUE;
338         }
339 }       
340
341 /*
342  * fill current host's address given an IP address
343  */
344 static void set_ipaddr(const char *addr)
345 {
346         strcpy(cur_host.orig, addr);
347         
348         if (!inet_aton(addr, &cur_host.addr))
349                 e_error = TRUE;
350         cur_host.mask.s_addr = ~0UL;
351 }
352
353 /*
354  * compute network bitmask
355  */
356 static unsigned long make_netmask(int bits) {
357         unsigned long buf = 0;
358         int i;
359
360         for (i=0; i<bits; i++)
361                 buf = (buf << 1) + 1;
362         for (; i < 32; i++)
363                 buf = (buf << 1);
364         return htonl(buf);
365 }
366
367 /*
368  * fill current host's address given IP address and netmask
369  */
370 static void set_ipnet(char *addr, int new)
371 {
372         char *pos, *net;
373
374         pos = strchr(addr, '/');
375         net = pos + 1;
376         *pos = 0;
377         
378         set_ipaddr(addr);
379         
380         if (new)
381                 cur_host.mask.s_addr = make_netmask(atoi(net));
382         else
383                 if (!inet_aton(net, &cur_host.mask))
384                         e_error = TRUE;
385
386         *pos = '/';
387         strcpy(cur_host.orig, addr);
388 }
389
390 /*
391  * add an option bit to the current host
392  */
393 static void add_option(const char *opt)
394 {
395         if (strcmp(opt,"no_root_squash") == 0)
396                 cur_host.options |= OPT_NO_ROOT_SQUASH;
397         else if (strcmp(opt,"root_squash") == 0)
398                 cur_host.options &= ~OPT_NO_ROOT_SQUASH;
399         else if (strcmp(opt,"all_squash") == 0)
400                 cur_host.options |= OPT_ALL_SQUASH;
401         else if (strcmp(opt,"no_all_squash") == 0)
402                 cur_host.options &= ~OPT_ALL_SQUASH;
403         else if (strcmp(opt,"rw") == 0)
404                 cur_host.options |= OPT_RW;
405         else if (strcmp(opt,"ro") == 0)
406                 cur_host.options &= ~OPT_RW;
407         else if (strcmp(opt,"removable") == 0) {
408                 cur_host.options |= OPT_REMOVABLE;
409         } else if (strcmp(opt,"fixed") == 0)
410                 cur_host.options &= ~OPT_REMOVABLE;
411         else if (strcmp(opt,"insecure") == 0)
412                 cur_host.options |= OPT_INSECURE;
413         else if (strcmp(opt,"secure") == 0)
414                 cur_host.options &= ~OPT_INSECURE;
415         else
416                 logmsg(LOG_WARNING, "Warning: unknown exports option `%s' ignored",
417                         opt);
418 }
419
420 static void add_option_with_value(const char *opt, const char *val)
421 {
422     if (strcmp(opt,"password") == 0) {
423         if (strlen(val) > PASSWORD_MAXLEN) {
424             logmsg(LOG_WARNING, "Warning: password for export %s truncated to 64 chars",
425                    cur_item.orig);
426         }
427         strncpy(cur_host.password, val, sizeof(password));
428         cur_host.password[PASSWORD_MAXLEN] = '\0';
429         /* Calculate hash */
430         cur_host.password_hash = fnv1a_32(cur_host.password, 0);
431     } else if (strcmp(opt,"anonuid") == 0) {
432         cur_host.anonuid = atoi(val);
433     } else if (strcmp(opt,"anongid") == 0) {
434         cur_host.anongid = atoi(val);
435     } else {
436         logmsg(LOG_WARNING, "Warning: unknown exports option `%s' ignored",
437             opt);
438     }
439 }
440
441 /*
442  * dummy error function
443  */
444 void yyerror(U(char *s))
445 {
446         e_error = TRUE;
447         return;
448 }
449
450 %}
451
452 %union {
453         char text[NFS_MAXPATHLEN];
454 };
455
456 %token <text> PATH
457 %token <text> ID
458 %token <text> OPTVALUE
459 %token <text> WHITE
460 %token <text> IP
461 %token <text> NET
462 %token <text> OLDNET
463
464 %%
465
466 exports:
467         export
468         | export '\n' exports
469         |
470         ;
471
472 export:
473         PATH                    { add_item($1); }
474         | PATH WHITE hosts      { add_item($1); }
475         | PATH WHITE            { add_item($1); }
476         ;
477
478 hosts:
479         host
480         | host WHITE hosts
481         | host WHITE
482         ;
483
484 host:
485         name                    { add_host(); }
486         | name '(' opts ')'     { add_host(); }
487         | '(' opts ')'          { add_host(); }
488         ;
489
490 name:
491         ID                      { set_hostname($1); }
492         | IP                    { set_ipaddr($1); }
493         | NET                   { set_ipnet($1, TRUE); }
494         | OLDNET                { set_ipnet($1, FALSE); }
495         ;
496         
497 opts:
498         opt                     
499         | opt ',' opts          
500         |
501         ;
502  
503 opt: 
504         ID                      { add_option($1); }
505         | ID OPTVALUE           { add_option_with_value($1,$2); } 
506         ;
507 %%
508
509 /*
510  * C code using yacc parser + access code for exports list
511  */
512
513 /* effective export list and access flag */
514 static e_item *export_list = NULL;
515 static volatile int exports_access = FALSE;
516
517 /* mount protocol compatible exports list */
518 exports exports_nfslist = NULL;
519
520 /*
521  * free NFS groups list
522  */
523 void free_nfsgroups(groups group)
524 {
525         groups list, next;
526         
527         list = group;
528         while (list) {
529                 next = (groups) list->gr_next;
530                 free(list);
531                 list = next;
532         }
533 }
534
535 /*
536  * free NFS exports list
537  */
538 void free_nfslist(exports elist)
539 {
540         exports list, next;
541
542         list = elist;
543         while(list) {
544                 next = (exports) list->ex_next;
545                 free_nfsgroups(list->ex_groups);
546                 free(list);
547                 list = next;
548         }
549 }
550
551 /*
552  * free list of host structures
553  */
554 static void free_hosts(e_item *item)
555 {
556         e_host *host, *cur;
557         
558         host = item->hosts;
559         while (host) {
560                 cur = host;
561                 host = (e_host *) host->next;
562                 free(cur);
563         }
564 }
565
566 /*
567  * free list of export items
568  */
569 static void free_list(e_item *item)
570 {
571         e_item *cur;
572         
573         while (item) {
574                 free_hosts(item);
575                 cur = item;
576                 item = (e_item *) item->next;
577                 free(cur);
578         }
579 }
580
581 /*
582  * print out the current exports list (for debugging)
583  */
584 void print_list(void)
585 {
586         char addrbuf[16], maskbuf[16];
587
588         e_item *item;
589         e_host *host;
590         
591         item = e_list;
592                 
593         while (item) {
594                 host = item->hosts;
595                 while (host) {
596                         /* inet_ntoa returns static buffer */
597                         strcpy(addrbuf, inet_ntoa(host->addr));
598                         strcpy(maskbuf, inet_ntoa(host->mask));
599                         printf("%s: ip %s mask %s options %i\n",
600                                 item->path, addrbuf, maskbuf,
601                                 host->options);
602                         host = (e_host *) host->next;
603                 }
604                 item = (e_item *) item->next;
605         }
606 }
607
608 /*
609  * clear current parse state
610  */
611 static void clear_cur(void)
612 {
613         e_list = NULL;
614         ne_list = NULL;
615         e_error = FALSE;
616         clear_host();
617         clear_item();
618 }
619
620 /*
621  * parse an exports file
622  */
623 int exports_parse(void)
624 {
625         FILE *efile;
626
627         /*
628          * if we are in the SIGHUP handler, a may_mount or get_options
629          * may currently be accessing the list
630          */
631         if (exports_access) {
632                 logmsg(LOG_CRIT, "export list is being traversed, no reload\n");
633                 return FALSE;
634         }
635
636         efile = fopen(opt_exports, "r");
637         if (!efile) {
638                 logmsg(LOG_CRIT, "could not open '%s', exporting nothing",
639                        opt_exports);
640                 free_list(export_list);
641                 free_nfslist(exports_nfslist);
642                 export_list = NULL;
643                 exports_nfslist = NULL;
644                 return FALSE;
645         }
646
647         yyin = efile;
648         clear_cur();
649         yyparse();
650         fclose(efile);
651         
652         if (e_error) {
653                 logmsg(LOG_CRIT, "syntax error in '%s', exporting nothing",
654                        opt_exports);
655                 free_list(export_list);
656                 free_nfslist(exports_nfslist);
657                 export_list = NULL;
658                 exports_nfslist = NULL;
659                 return FALSE;
660         }
661         
662         /* print out new list for debugging */
663         if (!opt_detach)
664                 print_list();
665         
666         free_list(export_list);
667         free_nfslist(exports_nfslist);
668         export_list = e_list;
669         exports_nfslist = ne_list;
670         return TRUE;
671 }
672
673 /*
674  * find a given host inside a host list, return options
675  */
676 static e_host* find_host(struct in_addr remote, e_item *item,
677                      char **password, uint32 *password_hash)
678 {
679         e_host *host;
680
681         host = item->hosts;
682         while (host) {
683                 if ((remote.s_addr & host->mask.s_addr) == host->addr.s_addr) {
684                         if (password != NULL) 
685                                 *password = host->password;
686                         if (password_hash != NULL)
687                                 *password_hash = host->password_hash;
688                         return host;
689                 }
690                 host = (e_host *) host->next;
691         }
692         return NULL;
693 }
694
695 /* options cache */
696 int exports_opts = -1;
697 const char *export_path = NULL; 
698 uint32 export_fsid = 0;
699 uint32 export_password_hash = 0;
700
701 /*
702  * given a path, return client's effective options
703  */
704 int exports_options(const char *path, struct svc_req *rqstp,
705                     char **password, uint32 *fsid)
706 {
707         e_item *list;
708         struct in_addr remote;
709         unsigned int last_len = 0;
710         
711         exports_opts = -1;
712         export_path = NULL;
713         export_fsid = 0;
714         last_anonuid = ANON_NOTSPECIAL;
715         last_anongid = ANON_NOTSPECIAL;
716
717         /* check for client attempting to use invalid pathname */
718         if (!path || strstr(path, "/../"))
719                 return exports_opts;
720         
721         remote = get_remote(rqstp);
722
723         /* protect against SIGHUP reloading the list */
724         exports_access = TRUE;
725         
726         list = export_list;
727         while (list) {
728                 /* longest matching prefix wins */
729                 if (strlen(list->path) > last_len    &&
730 #ifndef WIN32
731                     strstr(path, list->path) == path) {
732 #else
733                     !win_utf8ncasecmp(path, list->path, strlen(list->path))) {
734 #endif
735                     e_host* cur_host = find_host(remote, list, password, &export_password_hash);
736
737                         if (fsid != NULL)
738                                 *fsid = list->fsid;
739                         if (cur_host) {
740                                 exports_opts = cur_host->options;
741                                 export_path = list->path;
742                                 export_fsid = list->fsid;
743                                 last_len = strlen(list->path);
744                                 last_anonuid = cur_host->anonuid;
745                                 last_anongid = cur_host->anongid;
746                         }
747                 }
748                 list = (e_item *) list->next;
749         }
750         exports_access = FALSE;
751         return exports_opts;
752 }
753
754 /*
755  * check whether path is an export point
756  */
757 int export_point(const char *path)
758 {
759         e_item *list;
760
761         exports_access = TRUE;
762         list = export_list;
763
764         while (list) {
765             if (strcmp(path, list->path) == 0) {
766                 exports_access = FALSE;
767                 return TRUE;
768             }
769             list = (e_item *) list->next;
770         }
771         exports_access = FALSE;
772         return FALSE;
773 }
774
775 /*
776  * return exported path from static fsid
777  */
778 char *export_point_from_fsid(uint32 fsid, time_t **last_mtime, uint32 **dir_hash)
779 {
780     e_item *list;
781     
782     exports_access = TRUE;
783     list = export_list;
784     
785     while (list) {
786         if (list->fsid == fsid) {
787             if (last_mtime != NULL)
788                 *last_mtime = &list->last_mtime;
789             if (dir_hash != NULL)
790                 *dir_hash = &list->dir_hash;
791             exports_access = FALSE;
792             return list->path;
793         }
794         list = (e_item *) list->next;
795     }
796     exports_access = FALSE;
797     return NULL;
798 }
799
800
801 /*
802  * check whether export options of a path match with last set of options
803  */
804 nfsstat3 exports_compat(const char *path, struct svc_req *rqstp)
805 {
806         int prev;
807         uint32 prev_anonuid, prev_anongid;
808         
809         prev = exports_opts;
810         prev_anonuid = last_anonuid;
811         prev_anongid = last_anongid;
812         
813         if (exports_options(path, rqstp, NULL, NULL) == prev &&
814             last_anonuid == prev_anonuid &&
815             last_anongid == prev_anongid)
816                 return NFS3_OK;
817         else if (exports_opts == -1)
818                 return NFS3ERR_ACCES;
819         else
820                 return NFS3ERR_XDEV;
821 }
822
823 /*
824  * check whether options indicate rw mount
825  */
826 nfsstat3 exports_rw(void)
827 {
828         if (exports_opts != -1 && (exports_opts & OPT_RW))
829                 return NFS3_OK;
830         else
831                 return NFS3ERR_ROFS;
832 }
833
834 /*
835  * returns the last looked-up anonuid for a mount (ANON_NOTSPECIAL means none in effect)
836  */
837 uint32 exports_anonuid(void)
838 {
839         return last_anonuid;
840 }
841
842 /*
843  * returns the last looked-up anongid for a mount (ANON_NOTSPECIAL means none in effect)
844  */
845 uint32 exports_anongid(void)
846 {
847         return last_anongid;
848 }