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