]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/mountd/cache.c
c57d3b0a326a16adf01c4ee9b083f01df470fab3
[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 <time.h>
18 #include <netinet/in.h>
19 #include <arpa/inet.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <errno.h>
23 #include <ctype.h>
24 #include "misc.h"
25 #include "nfslib.h"
26 #include "exportfs.h"
27 #include "mountd.h"
28 #include "xmalloc.h"
29
30 /*
31  * Support routines for text-based upcalls.
32  * Fields are separated by spaces.
33  * Fields are either mangled to quote space tab newline slosh with slosh
34  * or a hexified with a leading \x
35  * Record is terminated with newline.
36  *
37  */
38 void cache_export_ent(char *domain, struct exportent *exp);
39
40
41 char *lbuf  = NULL;
42 int lbuflen = 0;
43
44 void auth_unix_ip(FILE *f)
45 {
46         /* requests are
47          *  class IP-ADDR
48          * Ignore if class != "nfsd"
49          * Otherwise find domainname and write back:
50          *
51          *  "nfsd" IP-ADDR expiry domainname
52          */
53         char *cp;
54         char class[20];
55         char ipaddr[20];
56         char *client;
57         struct in_addr addr;
58         if (readline(fileno(f), &lbuf, &lbuflen) != 1)
59                 return;
60
61         cp = lbuf;
62
63         if (qword_get(&cp, class, 20) <= 0 ||
64             strcmp(class, "nfsd") != 0)
65                 return;
66
67         if (qword_get(&cp, ipaddr, 20) <= 0)
68                 return;
69
70         if (inet_aton(ipaddr, &addr)==0)
71                 return;
72
73         auth_reload();
74
75         /* addr is a valid, interesting address, find the domain name... */
76         client = client_compose(addr);
77
78         
79         qword_print(f, "nfsd");
80         qword_print(f, ipaddr);
81         qword_printint(f, time(0)+30*60);
82         if (client)
83                 qword_print(f, *client?client:"DEFAULT");
84         qword_eol(f);
85
86         if (client && strcmp(ipaddr, client))
87                 mountlist_add(ipaddr, *client?client:"DEFAULT");
88
89         if (client) free(client);
90         
91 }
92
93 void nfsd_fh(FILE *f)
94 {
95         /* request are:
96          *  domain fsidtype fsid
97          * interpret fsid, find export point and options, and write:
98          *  domain fsidtype fsid expiry path
99          */
100         char *cp;
101         char *dom;
102         int fsidtype;
103         int fsidlen;
104         unsigned int dev, major=0, minor=0;
105         unsigned int inode=0;
106         unsigned int fsidnum=0;
107         char fsid[32];
108         struct exportent *found = NULL;
109         nfs_export *exp;
110         int i;
111         int dev_missing = 0;
112
113         if (readline(fileno(f), &lbuf, &lbuflen) != 1)
114                 return;
115
116         cp = lbuf;
117
118         dom = malloc(strlen(cp));
119         if (dom == NULL)
120                 return;
121         if (qword_get(&cp, dom, strlen(cp)) <= 0)
122                 goto out;
123         if (qword_get_int(&cp, &fsidtype) != 0)
124                 goto out;
125         if (fsidtype < 0 || fsidtype > 3)
126                 goto out; /* unknown type */
127         if ((fsidlen = qword_get(&cp, fsid, 32)) <= 0)
128                 goto out;
129         switch(fsidtype) {
130         case 0: /* 4 bytes: 2 major, 2 minor, 4 inode */
131                 if (fsidlen != 8)
132                         goto out;
133                 memcpy(&dev, fsid, 4);
134                 memcpy(&inode, fsid+4, 4);
135                 major = ntohl(dev)>>16;
136                 minor = ntohl(dev) & 0xFFFF;
137                 break;
138
139         case 1: /* 4 bytes - fsid */
140                 if (fsidlen != 4)
141                         goto out;
142                 memcpy(&fsidnum, fsid, 4);
143                 break;
144
145         case 2: /* 12 bytes: 4 major, 4 minor, 4 inode 
146                  * This format is never actually used but was
147                  * an historical accident
148                  */
149                 if (fsidlen != 12)
150                         goto out;
151                 memcpy(&dev, fsid, 4); major = ntohl(dev);
152                 memcpy(&dev, fsid+4, 4); minor = ntohl(dev);
153                 memcpy(&inode, fsid+8, 4);
154                 break;
155
156         case 3: /* 8 bytes: 4 byte packed device number, 4 inode */
157                 /* This is *host* endian, not net-byte-order, because
158                  * no-one outside this host has any business interpreting it
159                  */
160                 if (fsidlen != 8)
161                         goto out;
162                 memcpy(&dev, fsid, 4);
163                 memcpy(&inode, fsid+4, 4);
164                 major = (dev & 0xfff00) >> 8;
165                 minor = (dev & 0xff) | ((dev >> 12) & 0xfff00);
166                 break;
167
168         }
169
170         auth_reload();
171
172         /* Now determine export point for this fsid/domain */
173         for (i=0 ; i < MCL_MAXTYPES; i++) {
174                 for (exp = exportlist[i]; exp; exp = exp->m_next) {
175                         struct stat stb;
176
177                         if (!client_member(dom, exp->m_client->m_hostname))
178                                 continue;
179                         if (exp->m_export.e_mountpoint &&
180                             !is_mountpoint(exp->m_export.e_mountpoint[0]?
181                                            exp->m_export.e_mountpoint:
182                                            exp->m_export.e_path))
183                                 dev_missing ++;
184                         if (stat(exp->m_export.e_path, &stb) != 0)
185                                 continue;
186                         if (fsidtype == 1 &&
187                             ((exp->m_export.e_flags & NFSEXP_FSID) == 0 ||
188                              exp->m_export.e_fsid != fsidnum))
189                                 continue;
190                         if (fsidtype != 1) {
191                                 if (stb.st_ino != inode)
192                                         continue;
193                                 if (major != major(stb.st_dev) ||
194                                     minor != minor(stb.st_dev))
195                                         continue;
196                         }
197                         /* It's a match !! */
198                         if (!found)
199                                 found = &exp->m_export;
200                         else if (strcmp(found->e_path, exp->m_export.e_path)!= 0)
201                         {
202                                 xlog(L_WARNING, "%s and %s have same filehandle for %s, using first",
203                                      found->e_path, exp->m_export.e_path, dom);
204                         }
205                 }
206         }
207         if (found && 
208             found->e_mountpoint &&
209             !is_mountpoint(found->e_mountpoint[0]?
210                            found->e_mountpoint:
211                            found->e_path)) {
212                 /* Cannot export this yet 
213                  * should log a warning, but need to rate limit
214                    xlog(L_WARNING, "%s not exported as %d not a mountpoint",
215                    found->e_path, found->e_mountpoint);
216                  */
217                 /* FIXME we need to make sure we re-visit this later */
218                 goto out;
219         }
220         if (!found && dev_missing) {
221                 /* The missing dev could be what we want, so just be
222                  * quite rather than returning stale yet
223                  */
224                 goto out;
225         }
226
227         if (found)
228                 cache_export_ent(dom, found);
229
230         qword_print(f, dom);
231         qword_printint(f, fsidtype);
232         qword_printhex(f, fsid, fsidlen);
233         qword_printint(f, time(0)+30*60);
234         if (found)
235                 qword_print(f, found->e_path);
236         qword_eol(f);
237  out:
238         free(dom);
239         return;         
240 }
241
242 void nfsd_export(FILE *f)
243 {
244         /* requests are:
245          *  domain path
246          * determine export options and return:
247          *  domain path expiry flags anonuid anongid fsid
248          */
249
250         char *cp;
251         int i;
252         char *dom, *path;
253         nfs_export *exp, *found = NULL;
254
255
256         if (readline(fileno(f), &lbuf, &lbuflen) != 1)
257                 return;
258
259         cp = lbuf;
260         dom = malloc(strlen(cp));
261         path = malloc(strlen(cp));
262
263         if (!dom || !path)
264                 goto out;
265
266         if (qword_get(&cp, dom, strlen(lbuf)) <= 0)
267                 goto out;
268         if (qword_get(&cp, path, strlen(lbuf)) <= 0)
269                 goto out;
270
271         auth_reload();
272
273         /* now find flags for this export point in this domain */
274         for (i=0 ; i < MCL_MAXTYPES; i++) {
275                 for (exp = exportlist[i]; exp; exp = exp->m_next) {
276                         if (!client_member(dom, exp->m_client->m_hostname))
277                                 continue;
278                         if (strcmp(path, exp->m_export.e_path))
279                                 continue;
280                         if (!found)
281                                 found = exp;
282                         else {
283                                 xlog(L_WARNING, "%s exported to both %s and %s in %s",
284                                      path, exp->m_client->m_hostname, found->m_client->m_hostname,
285                                      dom);
286                         }
287                 }
288         }
289
290         qword_print(f, dom);
291         qword_print(f, path);
292         qword_printint(f, time(0)+30*60);
293         if (found) {
294                 qword_printint(f, found->m_export.e_flags);
295                 qword_printint(f, found->m_export.e_anonuid);
296                 qword_printint(f, found->m_export.e_anongid);
297                 qword_printint(f, found->m_export.e_fsid);
298                 mountlist_add(dom, path);
299         }
300         qword_eol(f);
301  out:
302         if (dom) free(dom);
303         if (path) free(path);
304 }
305
306
307 struct {
308         char *cache_name;
309         void (*cache_handle)(FILE *f);
310         FILE *f;
311 } cachelist[] = {
312         { "auth.unix.ip", auth_unix_ip},
313         { "nfsd.export", nfsd_export},
314         { "nfsd.fh", nfsd_fh},
315         { NULL, NULL }
316 };
317
318 void cache_open(void) 
319 {
320         int i;
321         for (i=0; cachelist[i].cache_name; i++ ){
322                 char path[100];
323                 sprintf(path, "/proc/net/rpc/%s/channel", cachelist[i].cache_name);
324                 cachelist[i].f = fopen(path, "r+");
325         }
326 }
327
328 void cache_set_fds(fd_set *fdset)
329 {
330         int i;
331         for (i=0; cachelist[i].cache_name; i++) {
332                 if (cachelist[i].f)
333                         FD_SET(fileno(cachelist[i].f), fdset);
334         }
335 }
336
337 int cache_process_req(fd_set *readfds) 
338 {
339         int i;
340         int cnt = 0;
341         for (i=0; cachelist[i].cache_name; i++) {
342                 if (cachelist[i].f != NULL &&
343                     FD_ISSET(fileno(cachelist[i].f), readfds)) {
344                         cnt++;
345                         cachelist[i].cache_handle(cachelist[i].f);
346                         FD_CLR(fileno(cachelist[i].f), readfds);
347                 }
348         }
349         return cnt;
350 }
351
352
353 /*
354  * Give IP->domain and domain+path->options to kernel
355  * % echo nfsd $IP  $[now+30*60] $domain > /proc/net/rpc/auth.unix.ip/channel
356  * % echo $domain $path $[now+30*60] $options $anonuid $anongid $fsid > /proc/net/rpc/nfsd.export/channel
357  */
358
359 void cache_export_ent(char *domain, struct exportent *exp)
360 {
361
362         FILE *f = fopen("/proc/net/rpc/nfsd.export/channel", "w");
363         if (!f)
364                 return;
365
366         qword_print(f, domain);
367         qword_print(f, exp->e_path);
368         qword_printint(f, time(0)+30*60);
369         qword_printint(f, exp->e_flags);
370         qword_printint(f, exp->e_anonuid);
371         qword_printint(f, exp->e_anongid);
372         qword_printint(f, exp->e_fsid);
373         qword_eol(f);
374
375         fclose(f);
376
377         mountlist_add(domain, exp->e_path);
378 }
379
380 void cache_export(nfs_export *exp)
381 {
382         FILE *f;
383
384         f = fopen("/proc/net/rpc/auth.unix.ip/channel", "w");
385         if (!f)
386                 return;
387
388         qword_print(f, "nfsd");
389         qword_print(f, inet_ntoa(exp->m_client->m_addrlist[0]));
390         qword_printint(f, time(0)+30*60);
391         qword_print(f, exp->m_client->m_hostname);
392         qword_eol(f);
393         
394         fclose(f);
395
396         if (strcmp(inet_ntoa(exp->m_client->m_addrlist[0]), exp->m_client->m_hostname))
397                 mountlist_add(inet_ntoa(exp->m_client->m_addrlist[0]), exp->m_client->m_hostname);
398
399         cache_export_ent(exp->m_client->m_hostname, &exp->m_export);
400 }
401
402 /* Get a filehandle.
403  * { 
404  *   echo $domain $path $length 
405  *   read filehandle <&0
406  * } <> /proc/fs/nfsd/filehandle
407  */
408 struct nfs_fh_len *
409 cache_get_filehandle(nfs_export *exp, int len, char *p)
410 {
411         FILE *f = fopen("/proc/fs/nfsd/filehandle", "r+");
412         char buf[200];
413         char *bp = buf;
414         int failed;
415         static struct nfs_fh_len fh;
416
417         if (!f)
418                 f = fopen("/proc/fs/nfs/filehandle", "r+");
419         if (!f)
420                 return NULL;
421
422         qword_print(f, exp->m_client->m_hostname);
423         qword_print(f, p);
424         qword_printint(f, len); 
425         qword_eol(f);
426         
427         failed = (fgets(buf, sizeof(buf), f) == NULL);
428         fclose(f);
429         if (failed)
430                 return NULL;
431         memset(fh.fh_handle, 0, sizeof(fh.fh_handle));
432         fh.fh_size = qword_get(&bp, (char *)fh.fh_handle, NFS3_FHSIZE);
433         return &fh;
434 }
435