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