]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/mountd/cache.c
45397db66dcd8c66d36aad9f6608456ab16cf1de
[nfs-utils.git] / utils / mountd / cache.c
1
2 /*
3  * Handle communication with knfsd internal cache
4  *
5  * We open /proc/net/rpc/{auth.unix.ip,nfsd.export,nfsd.fh}/channel
6  * and listen for requests (using my_svc_run)
7  * 
8  */
9
10 #ifdef HAVE_CONFIG_H
11 #include <config.h>
12 #endif
13
14 #include <sys/types.h>
15 #include <sys/select.h>
16 #include <sys/stat.h>
17 #include <sys/vfs.h>
18 #include <time.h>
19 #include <netinet/in.h>
20 #include <arpa/inet.h>
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <errno.h>
24 #include <ctype.h>
25 #include <pwd.h>
26 #include <grp.h>
27 #include <mntent.h>
28 #include "misc.h"
29 #include "nfslib.h"
30 #include "exportfs.h"
31 #include "mountd.h"
32 #include "xmalloc.h"
33 #include "fsloc.h"
34 #include "pseudoflavors.h"
35
36 #ifdef USE_BLKID
37 #include "blkid/blkid.h"
38 #endif
39
40
41 enum nfsd_fsid {
42         FSID_DEV = 0,
43         FSID_NUM,
44         FSID_MAJOR_MINOR,
45         FSID_ENCODE_DEV,
46         FSID_UUID4_INUM,
47         FSID_UUID8,
48         FSID_UUID16,
49         FSID_UUID16_INUM,
50 };
51
52 /*
53  * Support routines for text-based upcalls.
54  * Fields are separated by spaces.
55  * Fields are either mangled to quote space tab newline slosh with slosh
56  * or a hexified with a leading \x
57  * Record is terminated with newline.
58  *
59  */
60 int cache_export_ent(char *domain, struct exportent *exp, char *p);
61
62
63 char *lbuf  = NULL;
64 int lbuflen = 0;
65 extern int use_ipaddr;
66
67 void auth_unix_ip(FILE *f)
68 {
69         /* requests are
70          *  class IP-ADDR
71          * Ignore if class != "nfsd"
72          * Otherwise find domainname and write back:
73          *
74          *  "nfsd" IP-ADDR expiry domainname
75          */
76         char *cp;
77         char class[20];
78         char ipaddr[20];
79         char *client = NULL;
80         struct in_addr addr;
81         struct hostent *he = NULL;
82         if (readline(fileno(f), &lbuf, &lbuflen) != 1)
83                 return;
84
85         xlog(D_CALL, "auth_unix_ip: inbuf '%s'", lbuf);
86
87         cp = lbuf;
88
89         if (qword_get(&cp, class, 20) <= 0 ||
90             strcmp(class, "nfsd") != 0)
91                 return;
92
93         if (qword_get(&cp, ipaddr, 20) <= 0)
94                 return;
95
96         if (inet_aton(ipaddr, &addr)==0)
97                 return;
98
99         auth_reload();
100
101         /* addr is a valid, interesting address, find the domain name... */
102         if (!use_ipaddr) {
103                 he = client_resolve(addr);
104                 client = client_compose(he);
105         }
106         
107         qword_print(f, "nfsd");
108         qword_print(f, ipaddr);
109         qword_printint(f, time(0)+30*60);
110         if (use_ipaddr)
111                 qword_print(f, ipaddr);
112         else if (client)
113                 qword_print(f, *client?client:"DEFAULT");
114         qword_eol(f);
115         xlog(D_CALL, "auth_unix_ip: client %p '%s'", client, client?client: "DEFAULT");
116
117         if (client) free(client);
118         free(he);
119 }
120
121 void auth_unix_gid(FILE *f)
122 {
123         /* Request are
124          *  uid
125          * reply is
126          *  uid expiry count list of group ids
127          */
128         uid_t uid;
129         struct passwd *pw;
130         gid_t glist[100], *groups = glist;
131         int ngroups = 100;
132         int rv, i;
133         char *cp;
134
135         if (readline(fileno(f), &lbuf, &lbuflen) != 1)
136                 return;
137
138         cp = lbuf;
139         if (qword_get_uint(&cp, &uid) != 0)
140                 return;
141
142         pw = getpwuid(uid);
143         if (!pw)
144                 rv = -1;
145         else {
146                 rv = getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups);
147                 if (rv == -1 && ngroups >= 100) {
148                         groups = malloc(sizeof(gid_t)*ngroups);
149                         if (!groups)
150                                 rv = -1;
151                         else
152                                 rv = getgrouplist(pw->pw_name, pw->pw_gid,
153                                                   groups, &ngroups);
154                 }
155         }
156         qword_printuint(f, uid);
157         qword_printuint(f, time(0)+30*60);
158         if (rv >= 0) {
159                 qword_printuint(f, ngroups);
160                 for (i=0; i<ngroups; i++)
161                         qword_printuint(f, groups[i]);
162         } else
163                 qword_printuint(f, 0);
164         qword_eol(f);
165
166         if (groups != glist)
167                 free(groups);
168 }
169
170 #if USE_BLKID
171 static const char *get_uuid_blkdev(char *path)
172 {
173         static blkid_cache cache = NULL;
174         struct stat stb;
175         char *devname;
176         blkid_tag_iterate iter;
177         blkid_dev dev;
178         const char *type;
179         const char *val = NULL;
180
181         if (cache == NULL)
182                 blkid_get_cache(&cache, NULL);
183
184         if (stat(path, &stb) != 0)
185                 return NULL;
186         devname = blkid_devno_to_devname(stb.st_dev);
187         if (!devname)
188                 return NULL;
189         dev = blkid_get_dev(cache, devname, BLKID_DEV_NORMAL);
190         free(devname);
191         if (!dev)
192                 return NULL;
193         iter = blkid_tag_iterate_begin(dev);
194         if (!iter)
195                 return NULL;
196         while (blkid_tag_next(iter, &type, &val) == 0)
197                 if (strcmp(type, "UUID") == 0)
198                         break;
199         blkid_tag_iterate_end(iter);
200         return val;
201 }
202 #else
203 #define get_uuid_blkdev(path) (NULL)
204 #endif
205
206 int get_uuid(char *path, char *uuid, int uuidlen, char *u)
207 {
208         /* extract hex digits from uuidstr and compose a uuid
209          * of the given length (max 16), xoring bytes to make
210          * a smaller uuid.  Then compare with uuid
211          */
212         int i = 0;
213         const char *val = NULL;
214         char fsid_val[17];
215
216         if (path) {
217                 val = get_uuid_blkdev(path);
218                 if (!val) {
219                         struct statfs64 st;
220
221                         if (statfs64(path, &st))
222                                 return 0;
223                         if (!st.f_fsid.__val[0] && !st.f_fsid.__val[1])
224                                 return 0;
225                         snprintf(fsid_val, 17, "%08x%08x",
226                                  st.f_fsid.__val[0], st.f_fsid.__val[1]);
227                         val = fsid_val;
228                 }
229         } else {
230                 val = uuid;
231         }
232         
233         memset(u, 0, uuidlen);
234         for ( ; *val ; val++) {
235                 char c = *val;
236                 if (!isxdigit(c))
237                         continue;
238                 if (isalpha(c)) {
239                         if (isupper(c))
240                                 c = c - 'A' + 10;
241                         else
242                                 c = c - 'a' + 10;
243                 } else
244                         c = c - '0' + 0;
245                 if ((i&1) == 0)
246                         c <<= 4;
247                 u[i/2] ^= c;
248                 i++;
249                 if (i == uuidlen*2)
250                         i = 0;
251         }
252         return 1;
253 }
254
255 /* Iterate through /etc/mtab, finding mountpoints
256  * at or below a given path
257  */
258 static char *next_mnt(void **v, char *p)
259 {
260         FILE *f;
261         struct mntent *me;
262         int l = strlen(p);
263         if (*v == NULL) {
264                 f = setmntent("/etc/mtab", "r");
265                 *v = f;
266         } else
267                 f = *v;
268         while ((me = getmntent(f)) != NULL &&
269                (strncmp(me->mnt_dir, p, l) != 0 ||
270                 me->mnt_dir[l] != '/'))
271                 ;
272         if (me == NULL) {
273                 endmntent(f);
274                 *v = NULL;
275                 return NULL;
276         }
277         return me->mnt_dir;
278 }
279
280 void nfsd_fh(FILE *f)
281 {
282         /* request are:
283          *  domain fsidtype fsid
284          * interpret fsid, find export point and options, and write:
285          *  domain fsidtype fsid expiry path
286          */
287         char *cp;
288         char *dom;
289         int fsidtype;
290         int fsidlen;
291         unsigned int dev, major=0, minor=0;
292         unsigned int inode=0;
293         unsigned long long inode64;
294         unsigned int fsidnum=0;
295         char fsid[32];
296         struct exportent *found = NULL;
297         struct hostent *he = NULL;
298         struct in_addr addr;
299         char *found_path = NULL;
300         nfs_export *exp;
301         int i;
302         int dev_missing = 0;
303         int uuidlen = 0;
304         char *fhuuid = NULL;
305
306         if (readline(fileno(f), &lbuf, &lbuflen) != 1)
307                 return;
308
309         xlog(D_CALL, "nfsd_fh: inbuf '%s'", lbuf);
310
311         cp = lbuf;
312         
313         dom = malloc(strlen(cp));
314         if (dom == NULL)
315                 return;
316         if (qword_get(&cp, dom, strlen(cp)) <= 0)
317                 goto out;
318         if (qword_get_int(&cp, &fsidtype) != 0)
319                 goto out;
320         if (fsidtype < 0 || fsidtype > 7)
321                 goto out; /* unknown type */
322         if ((fsidlen = qword_get(&cp, fsid, 32)) <= 0)
323                 goto out;
324         switch(fsidtype) {
325         case FSID_DEV: /* 4 bytes: 2 major, 2 minor, 4 inode */
326                 if (fsidlen != 8)
327                         goto out;
328                 memcpy(&dev, fsid, 4);
329                 memcpy(&inode, fsid+4, 4);
330                 major = ntohl(dev)>>16;
331                 minor = ntohl(dev) & 0xFFFF;
332                 break;
333
334         case FSID_NUM: /* 4 bytes - fsid */
335                 if (fsidlen != 4)
336                         goto out;
337                 memcpy(&fsidnum, fsid, 4);
338                 break;
339
340         case FSID_MAJOR_MINOR: /* 12 bytes: 4 major, 4 minor, 4 inode 
341                  * This format is never actually used but was
342                  * an historical accident
343                  */
344                 if (fsidlen != 12)
345                         goto out;
346                 memcpy(&dev, fsid, 4); major = ntohl(dev);
347                 memcpy(&dev, fsid+4, 4); minor = ntohl(dev);
348                 memcpy(&inode, fsid+8, 4);
349                 break;
350
351         case FSID_ENCODE_DEV: /* 8 bytes: 4 byte packed device number, 4 inode */
352                 /* This is *host* endian, not net-byte-order, because
353                  * no-one outside this host has any business interpreting it
354                  */
355                 if (fsidlen != 8)
356                         goto out;
357                 memcpy(&dev, fsid, 4);
358                 memcpy(&inode, fsid+4, 4);
359                 major = (dev & 0xfff00) >> 8;
360                 minor = (dev & 0xff) | ((dev >> 12) & 0xfff00);
361                 break;
362
363         case FSID_UUID4_INUM: /* 4 byte inode number and 4 byte uuid */
364                 if (fsidlen != 8)
365                         goto out;
366                 memcpy(&inode, fsid, 4);
367                 uuidlen = 4;
368                 fhuuid = fsid+4;
369                 break;
370         case FSID_UUID8: /* 8 byte uuid */
371                 if (fsidlen != 8)
372                         goto out;
373                 uuidlen = 8;
374                 fhuuid = fsid;
375                 break;
376         case FSID_UUID16: /* 16 byte uuid */
377                 if (fsidlen != 16)
378                         goto out;
379                 uuidlen = 16;
380                 fhuuid = fsid;
381                 break;
382         case FSID_UUID16_INUM: /* 8 byte inode number and 16 byte uuid */
383                 if (fsidlen != 24)
384                         goto out;
385                 memcpy(&inode64, fsid, 8);
386                 inode = inode64;
387                 uuidlen = 16;
388                 fhuuid = fsid+8;
389                 break;
390         }
391
392         auth_reload();
393
394         /* Now determine export point for this fsid/domain */
395         for (i=0 ; i < MCL_MAXTYPES; i++) {
396                 nfs_export *next_exp;
397                 for (exp = exportlist[i].p_head; exp; exp = next_exp) {
398                         struct stat stb;
399                         char u[16];
400                         char *path;
401
402                         if (exp->m_export.e_flags & NFSEXP_CROSSMOUNT) {
403                                 static nfs_export *prev = NULL;
404                                 static void *mnt = NULL;
405                                 
406                                 if (prev == exp) {
407                                         /* try a submount */
408                                         path = next_mnt(&mnt, exp->m_export.e_path);
409                                         if (!path) {
410                                                 next_exp = exp->m_next;
411                                                 prev = NULL;
412                                                 continue;
413                                         }
414                                         next_exp = exp;
415                                 } else {
416                                         prev = exp;
417                                         mnt = NULL;
418                                         path = exp->m_export.e_path;
419                                         next_exp = exp;
420                                 }
421                         } else {
422                                 path = exp->m_export.e_path;
423                                 next_exp = exp->m_next;
424                         }
425
426                         if (!use_ipaddr && !client_member(dom, exp->m_client->m_hostname))
427                                 continue;
428                         if (exp->m_export.e_mountpoint &&
429                             !is_mountpoint(exp->m_export.e_mountpoint[0]?
430                                            exp->m_export.e_mountpoint:
431                                            exp->m_export.e_path))
432                                 dev_missing ++;
433                         if (stat(path, &stb) != 0)
434                                 continue;
435                         if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) {
436                                 continue;
437                         }
438                         switch(fsidtype){
439                         case FSID_DEV:
440                         case FSID_MAJOR_MINOR:
441                         case FSID_ENCODE_DEV:
442                                 if (stb.st_ino != inode)
443                                         continue;
444                                 if (major != major(stb.st_dev) ||
445                                     minor != minor(stb.st_dev))
446                                         continue;
447                                 break;
448                         case FSID_NUM:
449                                 if (((exp->m_export.e_flags & NFSEXP_FSID) == 0 ||
450                                      exp->m_export.e_fsid != fsidnum))
451                                         continue;
452                                 break;
453                         case FSID_UUID4_INUM:
454                         case FSID_UUID16_INUM:
455                                 if (stb.st_ino != inode)
456                                         continue;
457                                 goto check_uuid;
458                         case FSID_UUID8:
459                         case FSID_UUID16:
460                                 if (!is_mountpoint(path))
461                                         continue;
462                         check_uuid:
463                                 if (exp->m_export.e_uuid)
464                                         get_uuid(NULL, exp->m_export.e_uuid,
465                                                  uuidlen, u);
466                                 else if (get_uuid(path, NULL, uuidlen, u) == 0)
467                                         continue;
468
469                                 if (memcmp(u, fhuuid, uuidlen) != 0)
470                                         continue;
471                                 break;
472                         }
473                         if (use_ipaddr) {
474                                 if (he == NULL) {
475                                         if (!inet_aton(dom, &addr))
476                                                 goto out;
477                                         he = client_resolve(addr);
478                                 }
479                                 if (!client_check(exp->m_client, he))
480                                         continue;
481                         }
482                         /* It's a match !! */
483                         if (!found) {
484                                 found = &exp->m_export;
485                                 found_path = strdup(path);
486                                 if (found_path == NULL)
487                                         goto out;
488                         } else if (strcmp(found->e_path, exp->m_export.e_path)!= 0)
489                         {
490                                 xlog(L_WARNING, "%s and %s have same filehandle for %s, using first",
491                                      found_path, path, dom);
492                         }
493                 }
494         }
495         if (found && 
496             found->e_mountpoint &&
497             !is_mountpoint(found->e_mountpoint[0]?
498                            found->e_mountpoint:
499                            found->e_path)) {
500                 /* Cannot export this yet 
501                  * should log a warning, but need to rate limit
502                    xlog(L_WARNING, "%s not exported as %d not a mountpoint",
503                    found->e_path, found->e_mountpoint);
504                  */
505                 /* FIXME we need to make sure we re-visit this later */
506                 goto out;
507         }
508         if (!found && dev_missing) {
509                 /* The missing dev could be what we want, so just be
510                  * quite rather than returning stale yet
511                  */
512                 goto out;
513         }
514
515         if (found)
516                 if (cache_export_ent(dom, found, found_path) < 0)
517                         found = 0;
518
519         qword_print(f, dom);
520         qword_printint(f, fsidtype);
521         qword_printhex(f, fsid, fsidlen);
522         /* The fsid -> path lookup can be quite expensive as it
523          * potentially stats and reads lots of devices, and some of those
524          * might have spun-down.  The Answer is not likely to
525          * change underneath us, and an 'exportfs -f' can always
526          * remove this from the kernel, so use a really log
527          * timeout.  Maybe this should be configurable on the command
528          * line.
529          */
530         qword_printint(f, 0x7fffffff);
531         if (found)
532                 qword_print(f, found_path);
533         qword_eol(f);
534  out:
535         if (found_path)
536                 free(found_path);
537         if (he)
538                 free(he);
539         free(dom);
540         xlog(D_CALL, "nfsd_fh: found %p path %s", found, found ? found->e_path : NULL);
541         return;         
542 }
543
544 static void write_fsloc(FILE *f, struct exportent *ep, char *path)
545 {
546         struct servers *servers;
547
548         if (ep->e_fslocmethod == FSLOC_NONE)
549                 return;
550
551         servers = replicas_lookup(ep->e_fslocmethod, ep->e_fslocdata, path);
552         if (!servers)
553                 return;
554         qword_print(f, "fsloc");
555         qword_printint(f, servers->h_num);
556         if (servers->h_num >= 0) {
557                 int i;
558                 for (i=0; i<servers->h_num; i++) {
559                         qword_print(f, servers->h_mp[i]->h_host);
560                         qword_print(f, servers->h_mp[i]->h_path);
561                 }
562         }
563         qword_printint(f, servers->h_referral);
564         release_replicas(servers);
565 }
566
567 static void write_secinfo(FILE *f, struct exportent *ep, int flag_mask)
568 {
569         struct sec_entry *p;
570
571         for (p = ep->e_secinfo; p->flav; p++)
572                 ; /* Do nothing */
573         if (p == ep->e_secinfo) {
574                 /* There was no sec= option */
575                 return;
576         }
577         qword_print(f, "secinfo");
578         qword_printint(f, p - ep->e_secinfo);
579         for (p = ep->e_secinfo; p->flav; p++) {
580                 qword_printint(f, p->flav->fnum);
581                 qword_printint(f, p->flags & flag_mask);
582         }
583
584 }
585
586 static int dump_to_cache(FILE *f, char *domain, char *path, struct exportent *exp)
587 {
588         qword_print(f, domain);
589         qword_print(f, path);
590         qword_printint(f, time(0)+30*60);
591         if (exp) {
592                 int different_fs = strcmp(path, exp->e_path) != 0;
593                 int flag_mask = different_fs ? ~NFSEXP_FSID : ~0;
594
595                 qword_printint(f, exp->e_flags & flag_mask);
596                 qword_printint(f, exp->e_anonuid);
597                 qword_printint(f, exp->e_anongid);
598                 qword_printint(f, exp->e_fsid);
599                 write_fsloc(f, exp, path);
600                 write_secinfo(f, exp, flag_mask);
601                 if (exp->e_uuid == NULL || different_fs) {
602                         char u[16];
603                         if (get_uuid(path, NULL, 16, u)) {
604                                 qword_print(f, "uuid");
605                                 qword_printhex(f, u, 16);
606                         }
607                 } else {
608                         char u[16];
609                         get_uuid(NULL, exp->e_uuid, 16, u);
610                         qword_print(f, "uuid");
611                         qword_printhex(f, u, 16);
612                 }
613         }
614         return qword_eol(f);
615 }
616
617 static int is_subdirectory(char *subpath, char *path)
618 {
619         int l = strlen(path);
620
621         return strcmp(subpath, path) == 0
622                 || (strncmp(subpath, path, l) == 0 && subpath[l] == '/');
623 }
624
625 static int path_matches(nfs_export *exp, char *path)
626 {
627         if (exp->m_export.e_flags & NFSEXP_CROSSMOUNT)
628                 return is_subdirectory(path, exp->m_export.e_path);
629         return strcmp(path, exp->m_export.e_path) == 0;
630 }
631
632 static int client_matches(nfs_export *exp, char *dom, struct hostent *he)
633 {
634         if (use_ipaddr)
635                 return client_check(exp->m_client, he);
636         return client_member(dom, exp->m_client->m_hostname);
637 }
638
639 static int export_matches(nfs_export *exp, char *dom, char *path, struct hostent *he)
640 {
641         return path_matches(exp, path) && client_matches(exp, dom, he);
642 }
643
644 static nfs_export *lookup_export(char *dom, char *path, struct hostent *he)
645 {
646         nfs_export *exp;
647         nfs_export *found = NULL;
648         int found_type = 0;
649         int i;
650
651         for (i=0 ; i < MCL_MAXTYPES; i++) {
652                 for (exp = exportlist[i].p_head; exp; exp = exp->m_next) {
653                         if (!export_matches(exp, dom, path, he))
654                                 continue;
655                         if (!found) {
656                                 found = exp;
657                                 found_type = i;
658                                 continue;
659                         }
660
661                         /* Always prefer non-V4ROOT mounts */
662                         if (found->m_export.e_flags & NFSEXP_V4ROOT)
663                                 continue;
664
665                         /* If one is a CROSSMOUNT, then prefer the longest path */
666                         if (((found->m_export.e_flags & NFSEXP_CROSSMOUNT) ||
667                              (exp->m_export.e_flags & NFSEXP_CROSSMOUNT)) &&
668                             strlen(found->m_export.e_path) !=
669                             strlen(exp->m_export.e_path)) {
670
671                                 if (strlen(exp->m_export.e_path) >
672                                     strlen(found->m_export.e_path)) {
673                                         found = exp;
674                                         found_type = i;
675                                 }
676                                 continue;
677
678                         } else if (found_type == i && found->m_warned == 0) {
679                                 xlog(L_WARNING, "%s exported to both %s and %s, "
680                                      "arbitrarily choosing options from first",
681                                      path, found->m_client->m_hostname, exp->m_client->m_hostname,
682                                      dom);
683                                 found->m_warned = 1;
684                         }
685                 }
686         }
687         return found;
688 }
689
690 void nfsd_export(FILE *f)
691 {
692         /* requests are:
693          *  domain path
694          * determine export options and return:
695          *  domain path expiry flags anonuid anongid fsid
696          */
697
698         char *cp;
699         char *dom, *path;
700         nfs_export *found = NULL;
701         struct in_addr addr;
702         struct hostent *he = NULL;
703
704
705         if (readline(fileno(f), &lbuf, &lbuflen) != 1)
706                 return;
707
708         xlog(D_CALL, "nfsd_export: inbuf '%s'", lbuf);
709
710         cp = lbuf;
711         dom = malloc(strlen(cp));
712         path = malloc(strlen(cp));
713
714         if (!dom || !path)
715                 goto out;
716
717         if (qword_get(&cp, dom, strlen(lbuf)) <= 0)
718                 goto out;
719         if (qword_get(&cp, path, strlen(lbuf)) <= 0)
720                 goto out;
721
722         auth_reload();
723
724         if (use_ipaddr) {
725                 if (!inet_aton(dom, &addr))
726                         goto out;
727                 he = client_resolve(addr);
728         }
729
730         found = lookup_export(dom, path, he);
731
732         if (found) {
733                 if (dump_to_cache(f, dom, path, &found->m_export) < 0) {
734                         xlog(L_WARNING,
735                              "Cannot export %s, possibly unsupported filesystem"
736                              " or fsid= required", path);
737                         dump_to_cache(f, dom, path, NULL);
738                 }
739         } else {
740                 dump_to_cache(f, dom, path, NULL);
741         }
742  out:
743         xlog(D_CALL, "nfsd_export: found %p path %s", found, path ? path : NULL);
744         if (dom) free(dom);
745         if (path) free(path);
746         if (he) free(he);
747 }
748
749
750 struct {
751         char *cache_name;
752         void (*cache_handle)(FILE *f);
753         FILE *f;
754 } cachelist[] = {
755         { "auth.unix.ip", auth_unix_ip},
756         { "auth.unix.gid", auth_unix_gid},
757         { "nfsd.export", nfsd_export},
758         { "nfsd.fh", nfsd_fh},
759         { NULL, NULL }
760 };
761
762 extern int manage_gids;
763 void cache_open(void) 
764 {
765         int i;
766         for (i=0; cachelist[i].cache_name; i++ ) {
767                 char path[100];
768                 if (!manage_gids && cachelist[i].cache_handle == auth_unix_gid)
769                         continue;
770                 sprintf(path, "/proc/net/rpc/%s/channel", cachelist[i].cache_name);
771                 cachelist[i].f = fopen(path, "r+");
772         }
773 }
774
775 void cache_set_fds(fd_set *fdset)
776 {
777         int i;
778         for (i=0; cachelist[i].cache_name; i++) {
779                 if (cachelist[i].f)
780                         FD_SET(fileno(cachelist[i].f), fdset);
781         }
782 }
783
784 int cache_process_req(fd_set *readfds) 
785 {
786         int i;
787         int cnt = 0;
788         for (i=0; cachelist[i].cache_name; i++) {
789                 if (cachelist[i].f != NULL &&
790                     FD_ISSET(fileno(cachelist[i].f), readfds)) {
791                         cnt++;
792                         cachelist[i].cache_handle(cachelist[i].f);
793                         FD_CLR(fileno(cachelist[i].f), readfds);
794                 }
795         }
796         return cnt;
797 }
798
799
800 /*
801  * Give IP->domain and domain+path->options to kernel
802  * % echo nfsd $IP  $[now+30*60] $domain > /proc/net/rpc/auth.unix.ip/channel
803  * % echo $domain $path $[now+30*60] $options $anonuid $anongid $fsid > /proc/net/rpc/nfsd.export/channel
804  */
805
806 int cache_export_ent(char *domain, struct exportent *exp, char *path)
807 {
808         int err;
809         FILE *f = fopen("/proc/net/rpc/nfsd.export/channel", "w");
810         if (!f)
811                 return -1;
812
813         err = dump_to_cache(f, domain, exp->e_path, exp);
814         if (err) {
815                 xlog(L_WARNING,
816                      "Cannot export %s, possibly unsupported filesystem or"
817                      " fsid= required", exp->e_path);
818         }
819
820         while (err == 0 && (exp->e_flags & NFSEXP_CROSSMOUNT) && path) {
821                 /* really an 'if', but we can break out of
822                  * a 'while' more easily */
823                 /* Look along 'path' for other filesystems
824                  * and export them with the same options
825                  */
826                 struct stat stb;
827                 int l = strlen(exp->e_path);
828                 int dev;
829
830                 if (strlen(path) <= l || path[l] != '/' ||
831                     strncmp(exp->e_path, path, l) != 0)
832                         break;
833                 if (stat(exp->e_path, &stb) != 0)
834                         break;
835                 dev = stb.st_dev;
836                 while(path[l] == '/') {
837                         char c;
838                         /* errors for submount should fail whole filesystem */
839                         int err2;
840
841                         l++;
842                         while (path[l] != '/' && path[l])
843                                 l++;
844                         c = path[l];
845                         path[l] = 0;
846                         err2 = lstat(path, &stb);
847                         path[l] = c;
848                         if (err2 < 0)
849                                 break;
850                         if (stb.st_dev == dev)
851                                 continue;
852                         dev = stb.st_dev;
853                         path[l] = 0;
854                         dump_to_cache(f, domain, path, exp);
855                         path[l] = c;
856                 }
857                 break;
858         }
859
860         fclose(f);
861         return err;
862 }
863
864 int cache_export(nfs_export *exp, char *path)
865 {
866         int err;
867         FILE *f;
868
869         f = fopen("/proc/net/rpc/auth.unix.ip/channel", "w");
870         if (!f)
871                 return -1;
872
873         qword_print(f, "nfsd");
874         qword_print(f, inet_ntoa(exp->m_client->m_addrlist[0]));
875         qword_printint(f, time(0)+30*60);
876         qword_print(f, exp->m_client->m_hostname);
877         err = qword_eol(f);
878         
879         fclose(f);
880
881         err = cache_export_ent(exp->m_client->m_hostname, &exp->m_export, path)
882                 || err;
883         return err;
884 }
885
886 /* Get a filehandle.
887  * { 
888  *   echo $domain $path $length 
889  *   read filehandle <&0
890  * } <> /proc/fs/nfsd/filehandle
891  */
892 struct nfs_fh_len *
893 cache_get_filehandle(nfs_export *exp, int len, char *p)
894 {
895         FILE *f = fopen("/proc/fs/nfsd/filehandle", "r+");
896         char buf[200];
897         char *bp = buf;
898         int failed;
899         static struct nfs_fh_len fh;
900
901         if (!f)
902                 f = fopen("/proc/fs/nfs/filehandle", "r+");
903         if (!f)
904                 return NULL;
905
906         qword_print(f, exp->m_client->m_hostname);
907         qword_print(f, p);
908         qword_printint(f, len); 
909         failed = qword_eol(f);
910         
911         if (!failed)
912                 failed = (fgets(buf, sizeof(buf), f) == NULL);
913         fclose(f);
914         if (failed)
915                 return NULL;
916         memset(fh.fh_handle, 0, sizeof(fh.fh_handle));
917         fh.fh_size = qword_get(&bp, (char *)fh.fh_handle, NFS3_FHSIZE);
918         return &fh;
919 }
920