3 * Handle communication with knfsd internal cache
5 * We open /proc/net/rpc/{auth.unix.ip,nfsd.export,nfsd.fh}/channel
6 * and listen for requests (using my_svc_run)
14 #include <sys/types.h>
15 #include <sys/select.h>
18 #include <netinet/in.h>
19 #include <arpa/inet.h>
30 #include "blkid/blkid.h"
45 * Support routines for text-based upcalls.
46 * Fields are separated by spaces.
47 * Fields are either mangled to quote space tab newline slosh with slosh
48 * or a hexified with a leading \x
49 * Record is terminated with newline.
52 int cache_export_ent(char *domain, struct exportent *exp);
58 void auth_unix_ip(FILE *f)
62 * Ignore if class != "nfsd"
63 * Otherwise find domainname and write back:
65 * "nfsd" IP-ADDR expiry domainname
72 if (readline(fileno(f), &lbuf, &lbuflen) != 1)
77 if (qword_get(&cp, class, 20) <= 0 ||
78 strcmp(class, "nfsd") != 0)
81 if (qword_get(&cp, ipaddr, 20) <= 0)
84 if (inet_aton(ipaddr, &addr)==0)
89 /* addr is a valid, interesting address, find the domain name... */
90 client = client_compose(addr);
93 qword_print(f, "nfsd");
94 qword_print(f, ipaddr);
95 qword_printint(f, time(0)+30*60);
97 qword_print(f, *client?client:"DEFAULT");
100 if (client) free(client);
104 int get_uuid(char *path, char *uuid, int uuidlen, char *u)
106 /* extract hex digits from uuidstr and compose a uuid
107 * of the given length (max 16), xoring bytes to make
108 * a smaller uuid. Then compare with uuid
114 static blkid_cache cache = NULL;
117 blkid_tag_iterate iter;
121 blkid_get_cache(&cache, NULL);
123 blkid_probe_all_new(cache);
125 if (stat(path, &stb) != 0)
127 devname = blkid_devno_to_devname(stb.st_dev);
130 dev = blkid_get_dev(cache, devname, BLKID_DEV_NORMAL);
134 iter = blkid_tag_iterate_begin(dev);
137 while (blkid_tag_next(iter, &type, &val) == 0)
138 if (strcmp(type, "UUID") == 0)
140 blkid_tag_iterate_end(iter);
147 memset(u, 0, uuidlen);
148 for ( ; *val ; val++) {
170 void nfsd_fh(FILE *f)
173 * domain fsidtype fsid
174 * interpret fsid, find export point and options, and write:
175 * domain fsidtype fsid expiry path
181 unsigned int dev, major=0, minor=0;
182 unsigned int inode=0;
183 unsigned long long inode64;
184 unsigned int fsidnum=0;
186 struct exportent *found = NULL;
193 if (readline(fileno(f), &lbuf, &lbuflen) != 1)
198 dom = malloc(strlen(cp));
201 if (qword_get(&cp, dom, strlen(cp)) <= 0)
203 if (qword_get_int(&cp, &fsidtype) != 0)
205 if (fsidtype < 0 || fsidtype > 7)
206 goto out; /* unknown type */
207 if ((fsidlen = qword_get(&cp, fsid, 32)) <= 0)
210 case FSID_DEV: /* 4 bytes: 2 major, 2 minor, 4 inode */
213 memcpy(&dev, fsid, 4);
214 memcpy(&inode, fsid+4, 4);
215 major = ntohl(dev)>>16;
216 minor = ntohl(dev) & 0xFFFF;
219 case FSID_NUM: /* 4 bytes - fsid */
222 memcpy(&fsidnum, fsid, 4);
225 case FSID_MAJOR_MINOR: /* 12 bytes: 4 major, 4 minor, 4 inode
226 * This format is never actually used but was
227 * an historical accident
231 memcpy(&dev, fsid, 4); major = ntohl(dev);
232 memcpy(&dev, fsid+4, 4); minor = ntohl(dev);
233 memcpy(&inode, fsid+8, 4);
236 case FSID_ENCODE_DEV: /* 8 bytes: 4 byte packed device number, 4 inode */
237 /* This is *host* endian, not net-byte-order, because
238 * no-one outside this host has any business interpreting it
242 memcpy(&dev, fsid, 4);
243 memcpy(&inode, fsid+4, 4);
244 major = (dev & 0xfff00) >> 8;
245 minor = (dev & 0xff) | ((dev >> 12) & 0xfff00);
248 case FSID_UUID4_INUM: /* 4 byte inode number and 4 byte uuid */
251 memcpy(&inode, fsid, 4);
255 case FSID_UUID8: /* 8 byte uuid */
261 case FSID_UUID16: /* 16 byte uuid */
267 case FSID_UUID16_INUM: /* 8 byte inode number and 16 byte uuid */
270 memcpy(&inode64, fsid, 8);
279 /* Now determine export point for this fsid/domain */
280 for (i=0 ; i < MCL_MAXTYPES; i++) {
281 for (exp = exportlist[i]; exp; exp = exp->m_next) {
285 if (!client_member(dom, exp->m_client->m_hostname))
287 if (exp->m_export.e_mountpoint &&
288 !is_mountpoint(exp->m_export.e_mountpoint[0]?
289 exp->m_export.e_mountpoint:
290 exp->m_export.e_path))
292 if (stat(exp->m_export.e_path, &stb) != 0)
296 case FSID_MAJOR_MINOR:
297 case FSID_ENCODE_DEV:
298 if (stb.st_ino != inode)
300 if (major != major(stb.st_dev) ||
301 minor != minor(stb.st_dev))
305 if (((exp->m_export.e_flags & NFSEXP_FSID) == 0 ||
306 exp->m_export.e_fsid != fsidnum))
309 case FSID_UUID4_INUM:
310 case FSID_UUID16_INUM:
311 if (stb.st_ino != inode)
316 if (!is_mountpoint(exp->m_export.e_path))
319 if (exp->m_export.e_uuid)
320 get_uuid(NULL, exp->m_export.e_uuid,
322 else if (get_uuid(exp->m_export.e_path, NULL,
326 if (memcmp(u, fhuuid, uuidlen) != 0)
330 /* It's a match !! */
332 found = &exp->m_export;
333 else if (strcmp(found->e_path, exp->m_export.e_path)!= 0)
335 xlog(L_WARNING, "%s and %s have same filehandle for %s, using first",
336 found->e_path, exp->m_export.e_path, dom);
341 found->e_mountpoint &&
342 !is_mountpoint(found->e_mountpoint[0]?
345 /* Cannot export this yet
346 * should log a warning, but need to rate limit
347 xlog(L_WARNING, "%s not exported as %d not a mountpoint",
348 found->e_path, found->e_mountpoint);
350 /* FIXME we need to make sure we re-visit this later */
353 if (!found && dev_missing) {
354 /* The missing dev could be what we want, so just be
355 * quite rather than returning stale yet
361 cache_export_ent(dom, found);
364 qword_printint(f, fsidtype);
365 qword_printhex(f, fsid, fsidlen);
366 qword_printint(f, time(0)+30*60);
368 qword_print(f, found->e_path);
375 static int dump_to_cache(FILE *f, char *domain, char *path, struct exportent *exp)
377 qword_print(f, domain);
378 qword_print(f, path);
379 qword_printint(f, time(0)+30*60);
381 qword_printint(f, exp->e_flags);
382 qword_printint(f, exp->e_anonuid);
383 qword_printint(f, exp->e_anongid);
384 qword_printint(f, exp->e_fsid);
385 if (exp->e_uuid == NULL) {
387 if (get_uuid(exp->e_path, NULL, 16, u)) {
388 qword_print(f, "uuid");
389 qword_printhex(f, u, 16);
391 } else if (exp->e_uuid) {
392 qword_print(f, "uuid");
393 qword_printhex(f, exp->e_uuid, 16);
399 void nfsd_export(FILE *f)
403 * determine export options and return:
404 * domain path expiry flags anonuid anongid fsid
410 nfs_export *exp, *found = NULL;
413 if (readline(fileno(f), &lbuf, &lbuflen) != 1)
417 dom = malloc(strlen(cp));
418 path = malloc(strlen(cp));
423 if (qword_get(&cp, dom, strlen(lbuf)) <= 0)
425 if (qword_get(&cp, path, strlen(lbuf)) <= 0)
430 /* now find flags for this export point in this domain */
431 for (i=0 ; i < MCL_MAXTYPES; i++) {
432 for (exp = exportlist[i]; exp; exp = exp->m_next) {
433 if (!client_member(dom, exp->m_client->m_hostname))
435 if (strcmp(path, exp->m_export.e_path))
440 xlog(L_WARNING, "%s exported to both %s and %s in %s",
441 path, exp->m_client->m_hostname, found->m_client->m_hostname,
448 dump_to_cache(f, dom, path, &found->m_export);
449 mountlist_add(dom, path);
451 dump_to_cache(f, dom, path, NULL);
455 if (path) free(path);
461 void (*cache_handle)(FILE *f);
464 { "auth.unix.ip", auth_unix_ip},
465 { "nfsd.export", nfsd_export},
466 { "nfsd.fh", nfsd_fh},
470 void cache_open(void)
473 for (i=0; cachelist[i].cache_name; i++ ){
475 sprintf(path, "/proc/net/rpc/%s/channel", cachelist[i].cache_name);
476 cachelist[i].f = fopen(path, "r+");
480 void cache_set_fds(fd_set *fdset)
483 for (i=0; cachelist[i].cache_name; i++) {
485 FD_SET(fileno(cachelist[i].f), fdset);
489 int cache_process_req(fd_set *readfds)
493 for (i=0; cachelist[i].cache_name; i++) {
494 if (cachelist[i].f != NULL &&
495 FD_ISSET(fileno(cachelist[i].f), readfds)) {
497 cachelist[i].cache_handle(cachelist[i].f);
498 FD_CLR(fileno(cachelist[i].f), readfds);
506 * Give IP->domain and domain+path->options to kernel
507 * % echo nfsd $IP $[now+30*60] $domain > /proc/net/rpc/auth.unix.ip/channel
508 * % echo $domain $path $[now+30*60] $options $anonuid $anongid $fsid > /proc/net/rpc/nfsd.export/channel
511 int cache_export_ent(char *domain, struct exportent *exp)
514 FILE *f = fopen("/proc/net/rpc/nfsd.export/channel", "w");
518 err = dump_to_cache(f, domain, exp->e_path, exp);
520 mountlist_add(domain, exp->e_path);
524 int cache_export(nfs_export *exp)
529 if (exp->m_export.e_maptype != CLE_MAP_IDENT) {
530 xlog(L_ERROR, "%s: unsupported mapping; kernel supports only 'identity' (default)",
531 exp->m_export.m_path);
535 f = fopen("/proc/net/rpc/auth.unix.ip/channel", "w");
539 qword_print(f, "nfsd");
540 qword_print(f, inet_ntoa(exp->m_client->m_addrlist[0]));
541 qword_printint(f, time(0)+30*60);
542 qword_print(f, exp->m_client->m_hostname);
547 err = cache_export_ent(exp->m_client->m_hostname, &exp->m_export)
554 * echo $domain $path $length
555 * read filehandle <&0
556 * } <> /proc/fs/nfsd/filehandle
559 cache_get_filehandle(nfs_export *exp, int len, char *p)
561 FILE *f = fopen("/proc/fs/nfsd/filehandle", "r+");
565 static struct nfs_fh_len fh;
568 f = fopen("/proc/fs/nfs/filehandle", "r+");
572 qword_print(f, exp->m_client->m_hostname);
574 qword_printint(f, len);
575 failed = qword_eol(f);
578 failed = (fgets(buf, sizeof(buf), f) == NULL);
582 memset(fh.fh_handle, 0, sizeof(fh.fh_handle));
583 fh.fh_size = qword_get(&bp, (char *)fh.fh_handle, NFS3_FHSIZE);