3 * UNFS3 exports parser and export controls
4 * (C) 2004, Pascal Schmidt
5 * see file LICENSE for license details
13 #include "../winsupport.h"
18 #include <netinet/in.h>
19 #include <arpa/inet.h>
27 #include "../daemon.h"
28 #include "../backend.h"
32 # define PATH_MAX 4096
35 /* for lack of a better place */
37 #define U(x) x __attribute__ ((unused))
42 /* lexer stuff, to avoid compiler warnings */
47 * C code used by yacc parser
51 char orig[NFS_MAXPATHLEN];
53 char password[PASSWORD_MAXLEN+1];
63 char path[NFS_MAXPATHLEN];
64 char orig[NFS_MAXPATHLEN];
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) */
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;
77 /* last looked-up anonuid and anongid */
78 static uint32 last_anonuid = ANON_NOTSPECIAL;
79 static uint32 last_anongid = ANON_NOTSPECIAL;
81 /* mount protocol compatible variants */
82 static exports ne_list = NULL;
83 static struct exportnode ne_item;
84 static struct groupnode ne_host;
86 /* error status of last parse */
90 * The FNV1a-32 hash algorithm
91 * (http://www.isthe.com/chongo/tech/comp/fnv/)
93 uint32 fnv1a_32(const char *str, uint32 hval)
95 static const uint32 fnv_32_prime = 0x01000193;
105 uint32 wfnv1a_32(const wchar_t *str, uint32 hval)
107 static const uint32 fnv_32_prime = 0x01000193;
111 hval *= fnv_32_prime;
118 * get static fsid, for use with removable media export points
120 static uint32 get_free_fsid(const char *path)
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);
135 static void clear_host(void)
137 memset(&cur_host, 0, sizeof(e_host));
138 strcpy(cur_host.orig, "<anon clnt>");
139 memset(&ne_host, 0, sizeof(struct groupnode));
142 cur_host.anongid = ANON_NOTSPECIAL;
148 static void clear_item(void)
150 memset(&cur_item, 0, sizeof(e_item));
151 memset(&ne_item, 0, sizeof(struct exportnode));
155 * add current host to current export item
157 static void add_host(void)
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");
174 ne_new->gr_name = new->orig;
177 if (cur_item.hosts) {
178 iter = cur_item.hosts;
180 iter = (e_host *) iter->next;
181 iter->next = (struct e_host *) new;
183 cur_item.hosts = new;
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;
192 ne_item.ex_groups = ne_new;
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.
203 char *normpath(const char *path, char *normpath)
208 /* Copy path to normpath, and replace blocks of slashes with
213 /* Skip over multiple slashes */
214 if (*p == '/' && *(p+1) == '/') {
222 /* Remove trailing slash, if any. */
223 if ((n - normpath) > 1 && *(n-1) == '/')
230 * add current item to current export list
232 static void add_item(const char *path)
238 /* Is this item marked as removable for all hosts? */
239 int removable_for_all = 1;
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");
251 /* Loop over all hosts and check if marked as removable. */
252 host = cur_item.hosts;
254 if (!(host->options & OPT_REMOVABLE))
255 removable_for_all = 0;
256 host = (e_host *) host->next;
259 if (removable_for_all) {
260 /* If marked as removable for all hosts, don't try
263 } else if (!backend_realpath(path, buf)) {
264 logmsg(LOG_CRIT, "realpath for %s failed", path);
272 if (strlen(buf) + 1 > NFS_MAXPATHLEN) {
273 logmsg(LOG_CRIT, "attempted to export too long path");
281 /* if no hosts listed, list default host */
286 strcpy(new->path, buf);
287 strcpy(new->orig, path);
288 new->fsid = get_free_fsid(path);
293 ne_new->ex_dir = new->orig;
299 iter = (e_item *) iter->next;
300 iter->next = (struct e_item *) new;
304 /* matching mount protocol list */
307 while (ne_iter->ex_next)
308 ne_iter = (exports) ne_iter->ex_next;
309 ne_iter->ex_next = ne_new;
317 * fill current host's address given a hostname
319 static void set_hostname(const char *name)
323 if (strlen(name) + 1 > NFS_MAXPATHLEN) {
327 strcpy(cur_host.orig, name);
329 ent = gethostbyname(name);
332 memcpy(&cur_host.addr, ent->h_addr_list[0],
333 sizeof(struct in_addr));
334 cur_host.mask.s_addr = ~0UL;
336 logmsg(LOG_CRIT, "could not resolve hostname '%s'", name);
342 * fill current host's address given an IP address
344 static void set_ipaddr(const char *addr)
346 strcpy(cur_host.orig, addr);
348 if (!inet_aton(addr, &cur_host.addr))
350 cur_host.mask.s_addr = ~0UL;
354 * compute network bitmask
356 static unsigned long make_netmask(int bits) {
357 unsigned long buf = 0;
360 for (i=0; i<bits; i++)
361 buf = (buf << 1) + 1;
368 * fill current host's address given IP address and netmask
370 static void set_ipnet(char *addr, int new)
374 pos = strchr(addr, '/');
381 cur_host.mask.s_addr = make_netmask(atoi(net));
383 if (!inet_aton(net, &cur_host.mask))
387 strcpy(cur_host.orig, addr);
391 * add an option bit to the current host
393 static void add_option(const char *opt)
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;
416 logmsg(LOG_WARNING, "Warning: unknown exports option `%s' ignored",
420 static void add_option_with_value(const char *opt, const char *val)
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",
427 strncpy(cur_host.password, val, sizeof(password));
428 cur_host.password[PASSWORD_MAXLEN] = '\0';
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);
436 logmsg(LOG_WARNING, "Warning: unknown exports option `%s' ignored",
442 * dummy error function
444 void yyerror(U(char *s))
453 char text[NFS_MAXPATHLEN];
458 %token <text> OPTVALUE
468 | export '\n' exports
473 PATH { add_item($1); }
474 | PATH WHITE hosts { add_item($1); }
475 | PATH WHITE { add_item($1); }
486 | name '(' opts ')' { add_host(); }
487 | '(' opts ')' { add_host(); }
491 ID { set_hostname($1); }
492 | IP { set_ipaddr($1); }
493 | NET { set_ipnet($1, TRUE); }
494 | OLDNET { set_ipnet($1, FALSE); }
504 ID { add_option($1); }
505 | ID OPTVALUE { add_option_with_value($1,$2); }
510 * C code using yacc parser + access code for exports list
513 /* effective export list and access flag */
514 static e_item *export_list = NULL;
515 static volatile int exports_access = FALSE;
517 /* mount protocol compatible exports list */
518 exports exports_nfslist = NULL;
521 * free NFS groups list
523 void free_nfsgroups(groups group)
529 next = (groups) list->gr_next;
536 * free NFS exports list
538 void free_nfslist(exports elist)
544 next = (exports) list->ex_next;
545 free_nfsgroups(list->ex_groups);
552 * free list of host structures
554 static void free_hosts(e_item *item)
561 host = (e_host *) host->next;
567 * free list of export items
569 static void free_list(e_item *item)
576 item = (e_item *) item->next;
582 * print out the current exports list (for debugging)
584 void print_list(void)
586 char addrbuf[16], maskbuf[16];
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,
602 host = (e_host *) host->next;
604 item = (e_item *) item->next;
609 * clear current parse state
611 static void clear_cur(void)
621 * parse an exports file
623 int exports_parse(void)
628 * if we are in the SIGHUP handler, a may_mount or get_options
629 * may currently be accessing the list
631 if (exports_access) {
632 logmsg(LOG_CRIT, "export list is being traversed, no reload\n");
636 efile = fopen(opt_exports, "r");
638 logmsg(LOG_CRIT, "could not open '%s', exporting nothing",
640 free_list(export_list);
641 free_nfslist(exports_nfslist);
643 exports_nfslist = NULL;
653 logmsg(LOG_CRIT, "syntax error in '%s', exporting nothing",
655 free_list(export_list);
656 free_nfslist(exports_nfslist);
658 exports_nfslist = NULL;
662 /* print out new list for debugging */
666 free_list(export_list);
667 free_nfslist(exports_nfslist);
668 export_list = e_list;
669 exports_nfslist = ne_list;
674 * find a given host inside a host list, return options
676 static e_host* find_host(struct in_addr remote, e_item *item,
677 char **password, uint32 *password_hash)
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;
690 host = (e_host *) host->next;
696 int exports_opts = -1;
697 const char *export_path = NULL;
698 uint32 export_fsid = 0;
699 uint32 export_password_hash = 0;
702 * given a path, return client's effective options
704 int exports_options(const char *path, struct svc_req *rqstp,
705 char **password, uint32 *fsid)
708 struct in_addr remote;
709 unsigned int last_len = 0;
714 last_anonuid = ANON_NOTSPECIAL;
715 last_anongid = ANON_NOTSPECIAL;
717 /* check for client attempting to use invalid pathname */
718 if (!path || strstr(path, "/../"))
721 remote = get_remote(rqstp);
723 /* protect against SIGHUP reloading the list */
724 exports_access = TRUE;
728 /* longest matching prefix wins */
729 if (strlen(list->path) > last_len &&
731 strstr(path, list->path) == path) {
733 !win_utf8ncasecmp(path, list->path, strlen(list->path))) {
735 e_host* cur_host = find_host(remote, list, password, &export_password_hash);
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;
748 list = (e_item *) list->next;
750 exports_access = FALSE;
755 * check whether path is an export point
757 int export_point(const char *path)
761 exports_access = TRUE;
765 if (strcmp(path, list->path) == 0) {
766 exports_access = FALSE;
769 list = (e_item *) list->next;
771 exports_access = FALSE;
776 * return exported path from static fsid
778 char *export_point_from_fsid(uint32 fsid, time_t **last_mtime, uint32 **dir_hash)
782 exports_access = TRUE;
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;
794 list = (e_item *) list->next;
796 exports_access = FALSE;
802 * check whether export options of a path match with last set of options
804 nfsstat3 exports_compat(const char *path, struct svc_req *rqstp)
807 uint32 prev_anonuid, prev_anongid;
810 prev_anonuid = last_anonuid;
811 prev_anongid = last_anongid;
813 if (exports_options(path, rqstp, NULL, NULL) == prev &&
814 last_anonuid == prev_anonuid &&
815 last_anongid == prev_anongid)
817 else if (exports_opts == -1)
818 return NFS3ERR_ACCES;
824 * check whether options indicate rw mount
826 nfsstat3 exports_rw(void)
828 if (exports_opts != -1 && (exports_opts & OPT_RW))
835 * returns the last looked-up anonuid for a mount (ANON_NOTSPECIAL means none in effect)
837 uint32 exports_anonuid(void)
843 * returns the last looked-up anongid for a mount (ANON_NOTSPECIAL means none in effect)
845 uint32 exports_anongid(void)