]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/mountd/cache.c
9bbbfb3c3a6cc3a4c616eefd64a3a0d300004f9b
[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         int 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_int(&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_printint(f, uid);
157         qword_printint(f, time(0)+30*60);
158         if (rv >= 0) {
159                 qword_printint(f, ngroups);
160                 for (i=0; i<ngroups; i++)
161                         qword_printint(f, groups[i]);
162         } else
163                 qword_printint(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)
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);
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                 
594                 if (different_fs)
595                         qword_printint(f, exp->e_flags & ~NFSEXP_FSID);
596                 else
597                         qword_printint(f, exp->e_flags);
598                 qword_printint(f, exp->e_anonuid);
599                 qword_printint(f, exp->e_anongid);
600                 qword_printint(f, exp->e_fsid);
601                 write_fsloc(f, exp, path);
602                 write_secinfo(f, exp);
603                 if (exp->e_uuid == NULL || different_fs) {
604                         char u[16];
605                         if (get_uuid(path, NULL, 16, u)) {
606                                 qword_print(f, "uuid");
607                                 qword_printhex(f, u, 16);
608                         }
609                 } else {
610                         char u[16];
611                         get_uuid(NULL, exp->e_uuid, 16, u);
612                         qword_print(f, "uuid");
613                         qword_printhex(f, u, 16);
614                 }
615         }
616         return qword_eol(f);
617 }
618
619 void nfsd_export(FILE *f)
620 {
621         /* requests are:
622          *  domain path
623          * determine export options and return:
624          *  domain path expiry flags anonuid anongid fsid
625          */
626
627         char *cp;
628         int i;
629         char *dom, *path;
630         nfs_export *exp, *found = NULL;
631         int found_type = 0;
632         struct in_addr addr;
633         struct hostent *he = NULL;
634
635
636         if (readline(fileno(f), &lbuf, &lbuflen) != 1)
637                 return;
638
639         xlog(D_CALL, "nfsd_export: inbuf '%s'", lbuf);
640
641         cp = lbuf;
642         dom = malloc(strlen(cp));
643         path = malloc(strlen(cp));
644
645         if (!dom || !path)
646                 goto out;
647
648         if (qword_get(&cp, dom, strlen(lbuf)) <= 0)
649                 goto out;
650         if (qword_get(&cp, path, strlen(lbuf)) <= 0)
651                 goto out;
652
653         auth_reload();
654
655         /* now find flags for this export point in this domain */
656         for (i=0 ; i < MCL_MAXTYPES; i++) {
657                 for (exp = exportlist[i].p_head; exp; exp = exp->m_next) {
658                         if (!use_ipaddr && !client_member(dom, exp->m_client->m_hostname))
659                                 continue;
660                         if (exp->m_export.e_flags & NFSEXP_CROSSMOUNT) {
661                                 /* if path is a mountpoint below e_path, then OK */
662                                 int l = strlen(exp->m_export.e_path);
663                                 if (strcmp(path, exp->m_export.e_path) == 0 ||
664                                     (strncmp(path, exp->m_export.e_path, l) == 0 &&
665                                      path[l] == '/' &&
666                                      is_mountpoint(path)))
667                                         /* ok */;
668                                 else
669                                         continue;
670                         } else if (strcmp(path, exp->m_export.e_path) != 0)
671                                 continue;
672                         if (use_ipaddr) {
673                                 if (he == NULL) {
674                                         if (!inet_aton(dom, &addr))
675                                                 goto out;
676                                         he = client_resolve(addr);
677                                 }
678                                 if (!client_check(exp->m_client, he))
679                                         continue;
680                         }
681                         if (!found) {
682                                 found = exp;
683                                 found_type = i;
684                                 continue;
685                         }
686                         /* If one is a CROSSMOUNT, then prefer the longest path */
687                         if (((found->m_export.e_flags & NFSEXP_CROSSMOUNT) ||
688                              (exp->m_export.e_flags & NFSEXP_CROSSMOUNT)) &&
689                             strlen(found->m_export.e_path) !=
690                             strlen(exp->m_export.e_path)) {
691
692                                 if (strlen(exp->m_export.e_path) >
693                                     strlen(found->m_export.e_path)) {
694                                         found = exp;
695                                         found_type = i;
696                                 }
697                                 continue;
698
699                         } else if (found_type == i && found->m_warned == 0) {
700                                 xlog(L_WARNING, "%s exported to both %s and %s, "
701                                      "arbitrarily choosing options from first",
702                                      path, found->m_client->m_hostname, exp->m_client->m_hostname,
703                                      dom);
704                                 found->m_warned = 1;
705                         }
706                 }
707         }
708
709         if (found) {
710                 if (dump_to_cache(f, dom, path, &found->m_export) < 0) {
711                         xlog(L_WARNING,
712                              "Cannot export %s, possibly unsupported filesystem"
713                              " or fsid= required", path);
714                         dump_to_cache(f, dom, path, NULL);
715                 }
716         } else {
717                 dump_to_cache(f, dom, path, NULL);
718         }
719  out:
720         xlog(D_CALL, "nfsd_export: found %p path %s", found, path ? path : NULL);
721         if (dom) free(dom);
722         if (path) free(path);
723         if (he) free(he);
724 }
725
726
727 struct {
728         char *cache_name;
729         void (*cache_handle)(FILE *f);
730         FILE *f;
731 } cachelist[] = {
732         { "auth.unix.ip", auth_unix_ip},
733         { "auth.unix.gid", auth_unix_gid},
734         { "nfsd.export", nfsd_export},
735         { "nfsd.fh", nfsd_fh},
736         { NULL, NULL }
737 };
738
739 extern int manage_gids;
740 void cache_open(void) 
741 {
742         int i;
743         for (i=0; cachelist[i].cache_name; i++ ) {
744                 char path[100];
745                 if (!manage_gids && cachelist[i].cache_handle == auth_unix_gid)
746                         continue;
747                 sprintf(path, "/proc/net/rpc/%s/channel", cachelist[i].cache_name);
748                 cachelist[i].f = fopen(path, "r+");
749         }
750 }
751
752 void cache_set_fds(fd_set *fdset)
753 {
754         int i;
755         for (i=0; cachelist[i].cache_name; i++) {
756                 if (cachelist[i].f)
757                         FD_SET(fileno(cachelist[i].f), fdset);
758         }
759 }
760
761 int cache_process_req(fd_set *readfds) 
762 {
763         int i;
764         int cnt = 0;
765         for (i=0; cachelist[i].cache_name; i++) {
766                 if (cachelist[i].f != NULL &&
767                     FD_ISSET(fileno(cachelist[i].f), readfds)) {
768                         cnt++;
769                         cachelist[i].cache_handle(cachelist[i].f);
770                         FD_CLR(fileno(cachelist[i].f), readfds);
771                 }
772         }
773         return cnt;
774 }
775
776
777 /*
778  * Give IP->domain and domain+path->options to kernel
779  * % echo nfsd $IP  $[now+30*60] $domain > /proc/net/rpc/auth.unix.ip/channel
780  * % echo $domain $path $[now+30*60] $options $anonuid $anongid $fsid > /proc/net/rpc/nfsd.export/channel
781  */
782
783 int cache_export_ent(char *domain, struct exportent *exp, char *path)
784 {
785         int err;
786         FILE *f = fopen("/proc/net/rpc/nfsd.export/channel", "w");
787         if (!f)
788                 return -1;
789
790         err = dump_to_cache(f, domain, exp->e_path, exp);
791         if (err) {
792                 xlog(L_WARNING,
793                      "Cannot export %s, possibly unsupported filesystem or"
794                      " fsid= required", exp->e_path);
795         }
796
797         while (err == 0 && (exp->e_flags & NFSEXP_CROSSMOUNT) && path) {
798                 /* really an 'if', but we can break out of
799                  * a 'while' more easily */
800                 /* Look along 'path' for other filesystems
801                  * and export them with the same options
802                  */
803                 struct stat stb;
804                 int l = strlen(exp->e_path);
805                 int dev;
806
807                 if (strlen(path) <= l || path[l] != '/' ||
808                     strncmp(exp->e_path, path, l) != 0)
809                         break;
810                 if (stat(exp->e_path, &stb) != 0)
811                         break;
812                 dev = stb.st_dev;
813                 while(path[l] == '/') {
814                         char c;
815                         /* errors for submount should fail whole filesystem */
816                         int err2;
817
818                         l++;
819                         while (path[l] != '/' && path[l])
820                                 l++;
821                         c = path[l];
822                         path[l] = 0;
823                         err2 = lstat(path, &stb);
824                         path[l] = c;
825                         if (err2 < 0)
826                                 break;
827                         if (stb.st_dev == dev)
828                                 continue;
829                         dev = stb.st_dev;
830                         path[l] = 0;
831                         dump_to_cache(f, domain, path, exp);
832                         path[l] = c;
833                 }
834                 break;
835         }
836
837         fclose(f);
838         return err;
839 }
840
841 int cache_export(nfs_export *exp, char *path)
842 {
843         int err;
844         FILE *f;
845
846         f = fopen("/proc/net/rpc/auth.unix.ip/channel", "w");
847         if (!f)
848                 return -1;
849
850         qword_print(f, "nfsd");
851         qword_print(f, inet_ntoa(exp->m_client->m_addrlist[0]));
852         qword_printint(f, time(0)+30*60);
853         qword_print(f, exp->m_client->m_hostname);
854         err = qword_eol(f);
855         
856         fclose(f);
857
858         err = cache_export_ent(exp->m_client->m_hostname, &exp->m_export, path)
859                 || err;
860         return err;
861 }
862
863 /* Get a filehandle.
864  * { 
865  *   echo $domain $path $length 
866  *   read filehandle <&0
867  * } <> /proc/fs/nfsd/filehandle
868  */
869 struct nfs_fh_len *
870 cache_get_filehandle(nfs_export *exp, int len, char *p)
871 {
872         FILE *f = fopen("/proc/fs/nfsd/filehandle", "r+");
873         char buf[200];
874         char *bp = buf;
875         int failed;
876         static struct nfs_fh_len fh;
877
878         if (!f)
879                 f = fopen("/proc/fs/nfs/filehandle", "r+");
880         if (!f)
881                 return NULL;
882
883         qword_print(f, exp->m_client->m_hostname);
884         qword_print(f, p);
885         qword_printint(f, len); 
886         failed = qword_eol(f);
887         
888         if (!failed)
889                 failed = (fgets(buf, sizeof(buf), f) == NULL);
890         fclose(f);
891         if (failed)
892                 return NULL;
893         memset(fh.fh_handle, 0, sizeof(fh.fh_handle));
894         fh.fh_size = qword_get(&bp, (char *)fh.fh_handle, NFS3_FHSIZE);
895         return &fh;
896 }
897