From 16be8c20d6fe1a997d90ecb229aee6a5f092b73d Mon Sep 17 00:00:00 2001 From: neilbrown Date: Wed, 26 Mar 2003 03:05:39 +0000 Subject: [PATCH] Work towards support new cache in 2.5 --- ChangeLog | 22 +++ support/export/client.c | 98 ++++++++++ support/export/hostname.c | 51 +++++ support/export/xtab.c | 47 ++++- support/include/exportfs.h | 3 + support/include/misc.h | 1 + support/include/nfs/export.h | 5 +- support/include/nfslib.h | 7 + support/nfs/cacheio.c | 54 ++++++ support/nfs/exports.c | 8 +- utils/exportfs/exportfs.c | 34 +++- utils/mountd/Makefile | 2 +- utils/mountd/auth.c | 128 ++++++------- utils/mountd/cache.c | 358 +++++++++++++++++++++++++++++++++++ utils/mountd/mountd.c | 31 ++- utils/mountd/rmtab.c | 2 +- utils/mountd/svc_run.c | 89 +++++++++ 17 files changed, 857 insertions(+), 83 deletions(-) create mode 100644 utils/mountd/cache.c create mode 100644 utils/mountd/svc_run.c diff --git a/ChangeLog b/ChangeLog index 8b3740b..d19ca83 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,25 @@ +2003-03-26 NeilBrown - devel branch + * support/nfs/cacheio.c: Add cache_flush and check_new_cache + * utils/exportfs/exportfs.c: Use check_new_cache and cache_flush + Don't actually talk to kernel when new cache is in effect, except + to flush + * utils/exportfs/exportfs.c: add -f option to fully flush cache. + * support/export/xtab.c: Only rename new xtab into place if it has + changed, thus preserving modify date for exportfs to use when + flushing. + * support/nfs/exports.c: make CROSSMNT distinct from NOHIDE + * utils/exportfs/exportfs.c: ditto + * support/include/nfs/export.h: ditto + * support/export/hostname.c: Define get_reliable_hostbyaddr + * support/include/misc.h: Declare that + * support/export/client.c: Add client_compose, client_member to + handle new composite client names + * support/include/exportfs.h: Declare those functions. + * support/utils/mountd/cache.c: new file + * support/utils/mountd/auth.c: substantial changes for new cache + support + * suppert/utils/mountd/mountd.c: changes to support new cache + 2003-03-26 NeilBrown * configure.in (VERSION): Set to "1.0.3". diff --git a/support/export/client.c b/support/export/client.c index 6d5d306..3db21ae 100644 --- a/support/export/client.c +++ b/support/export/client.c @@ -229,6 +229,104 @@ client_find(struct hostent *hp) return NULL; } +/* + * Find client name given an IP address + * This is found by gathering all known names that match that IP address, + * sorting them and joining them with '+' + * + */ +static char *add_name(char *old, char *add); + +char * +client_compose(struct in_addr addr) +{ + struct hostent *he = NULL; + char *name = NULL; + int i; + + if (clientlist[MCL_WILDCARD] || clientlist[MCL_NETGROUP]) + he = get_reliable_hostbyaddr((const char*)&addr, sizeof(addr), AF_INET); + if (he == NULL) + he = get_hostent((const char*)&addr, sizeof(addr), AF_INET); + + for (i = 0 ; i < MCL_MAXTYPES; i++) { + nfs_client *clp; + for (clp = clientlist[i]; clp ; clp = clp->m_next) { + if (!client_check(clp, he)) + continue; + name = add_name(name, clp->m_hostname); + } + } + return name; +} + +int +client_member(char *client, char *name) +{ + /* check if "client" (a ',' separated list of names) + * contains 'name' as a member + */ + int l = strlen(name); + while (*client) { + if (strncmp(client, name, l) == 0 && + (client[l] == ',' || client[l] == '\0')) + return 1; + client = strchr(client, ','); + if (client == NULL) + return 0; + client++; + } + return 0; +} + + +int +name_cmp(char *a, char *b) +{ + /* compare strings a and b, but only upto ',' in a */ + while (*a && *b && *a != ',' && *a == *b) + a++, b++; + if (!*b && (!*a || !a == ',') ) + return 0; + if (!*b) return 1; + if (!*a || *a == ',') return -1; + return *a - *b; +} + +static char * +add_name(char *old, char *add) +{ + int len = strlen(add)+2; + char *new; + char *cp; + if (old) len += strlen(old); + + new = malloc(len); + if (!new) { + free(old); + return NULL; + } + cp = old; + while (cp && *cp && name_cmp(cp, add) < 0) { + /* step cp forward over a name */ + char *e = strchr(cp, ','); + if (e) + cp = e+1; + else + cp = cp + strlen(cp); + } + strncpy(new, old, cp-old); + new[cp-old] = 0; + if (cp != old && !*cp) + strcat(new, ","); + strcat(new, add); + if (cp && *cp) { + strcat(new, ","); + strcat(new, cp); + } + return new; +} + /* * Match a host (given its hostent record) to a client record. This * is usually called from mountd. diff --git a/support/export/hostname.c b/support/export/hostname.c index 299fe99..0378f35 100644 --- a/support/export/hostname.c +++ b/support/export/hostname.c @@ -16,6 +16,7 @@ #include #include #include +#include #ifdef TEST #define xmalloc malloc #else @@ -216,6 +217,56 @@ matchhostname (const char *h1, const char *h2) return status; } + +/* Map IP to hostname, and then map back to addr to make sure it is a + * reliable hostname + */ +struct hostent * +get_reliable_hostbyaddr(const char *addr, int len, int type) +{ + struct hostent *hp; + + char **sp; + struct hostent *forward = NULL; + char *tmpname; + + hp = gethostbyaddr(addr, len , type); + if (!hp) + return hp; + + /* must make sure the hostent is authorative. */ + + hp = hostent_dup (hp); + tmpname = xstrdup((hp)->h_name); + if (tmpname) { + forward = gethostbyname(tmpname); + free(tmpname); + } + if (forward) { + /* now make sure the "addr" is in the list */ + for (sp = forward->h_addr_list ; *sp ; sp++) { + if (memcmp(*sp, addr, forward->h_length)==0) + break; + } + + if (!*sp) { + /* it was a FAKE */ + xlog(L_WARNING, "Fake hostname %s for %s - forward lookup doesn't match reverse", + forward->h_name, inet_ntoa(*(struct in_addr*)addr)); + return NULL; + } + free (hp); + hp = hostent_dup (forward); + } + else { + /* never heard of it. misconfigured DNS? */ + xlog(L_WARNING, "Fake hostname %s for %s - forward lookup doesn't exist", + forward->h_name, inet_ntoa(*(struct in_addr*)addr)); + return NULL; + } + return hp; +} + #ifdef TEST void print_host (struct hostent *hp) diff --git a/support/export/xtab.c b/support/export/xtab.c index 3ef3661..d9265a2 100644 --- a/support/export/xtab.c +++ b/support/export/xtab.c @@ -18,6 +18,8 @@ #include "xio.h" #include "xlog.h" +static void cond_rename(char *newfile, char *oldfile); + static int xtab_read(char *xtab, int is_export) { @@ -104,7 +106,7 @@ xtab_write(char *xtab, char *xtabtmp, int is_export) } endexportent(); - rename(xtabtmp, xtab); + cond_rename(xtabtmp, xtab); xfunlock(lockid); @@ -142,3 +144,46 @@ xtab_append(nfs_export *exp) exp->m_xtabent = 1; } +/* + * rename newfile onto oldfile unless + * they are identical + */ +static void cond_rename(char *newfile, char *oldfile) +{ + int nfd, ofd; + char nbuf[4096], obuf[4096]; + int ncnt, ocnt; + + nfd = open(newfile, 0); + if (nfd < 0) + return; + ofd = open(oldfile, 0); + if (ofd < 0) { + close(nfd); + rename(newfile, oldfile); + return; + } + + do { + ncnt = read(nfd, nbuf, sizeof(nbuf)); + if (ncnt < 0) + break; + ocnt = read(ofd, obuf, sizeof(obuf)); + if (ocnt < 0) + break; + if (ncnt != ocnt) + break; + if (ncnt == 0) { + close(nfd); + close(ofd); + unlink(newfile); + return; + } + } while (memcmp(obuf, nbuf, ncnt) == 0); + + /* some mis-match */ + close(nfd); + close(ofd); + rename(newfile, oldfile); + return; +} diff --git a/support/include/exportfs.h b/support/include/exportfs.h index 4021e60..0c70290 100644 --- a/support/include/exportfs.h +++ b/support/include/exportfs.h @@ -54,6 +54,9 @@ int client_check(nfs_client *, struct hostent *); int client_match(nfs_client *, char *hname); void client_release(nfs_client *); void client_freeall(void); +char * client_compose(struct in_addr addr); +int client_member(char *client, char *name); + int export_read(char *fname); void export_add(nfs_export *); diff --git a/support/include/misc.h b/support/include/misc.h index a3cdcfd..7d099d0 100644 --- a/support/include/misc.h +++ b/support/include/misc.h @@ -20,5 +20,6 @@ int matchhostname(const char *h1, const char *h2); struct hostent; struct hostent *hostent_dup(struct hostent *hp); struct hostent *get_hostent (const char *addr, int len, int type); +struct hostent *get_reliable_hostbyaddr(const char *addr, int len, int type); #endif /* MISC_H */ diff --git a/support/include/nfs/export.h b/support/include/nfs/export.h index 344ece5..d79a179 100644 --- a/support/include/nfs/export.h +++ b/support/include/nfs/export.h @@ -20,10 +20,11 @@ #define NFSEXP_UIDMAP 0x0040 #define NFSEXP_KERBEROS 0x0080 /* not available */ #define NFSEXP_SUNSECURE 0x0100 -#define NFSEXP_CROSSMNT 0x0200 +#define NFSEXP_NOHIDE 0x0200 #define NFSEXP_NOSUBTREECHECK 0x0400 #define NFSEXP_NOAUTHNLM 0x0800 #define NFSEXP_FSID 0x2000 -#define NFSEXP_ALLFLAGS 0x3FFF +#define NFSEXP_CROSSMNT 0x4000 +#define NFSEXP_ALLFLAGS 0x7FFF #endif /* _NSF_EXPORT_H */ diff --git a/support/include/nfslib.h b/support/include/nfslib.h index 90bd33a..f896933 100644 --- a/support/include/nfslib.h +++ b/support/include/nfslib.h @@ -126,6 +126,13 @@ void qword_print(FILE *f, char *str); void qword_printhex(FILE *f, char *str, int slen); void qword_printint(FILE *f, int num); void qword_eol(FILE *f); +int readline(int fd, char **buf, int *lenp); +int qword_get(char **bpp, char *dest, int bufsize); +int qword_get_int(char **bpp, int *anint); + +void cache_flush(int force); +int check_new_cache(void); + /* lockd. */ int lockdsvc(); diff --git a/support/nfs/cacheio.c b/support/nfs/cacheio.c index 960d801..69373b9 100644 --- a/support/nfs/cacheio.c +++ b/support/nfs/cacheio.c @@ -19,6 +19,10 @@ #include #include #include +#include +#include +#include +#include void qword_add(char **bpp, int *lp, char *str) { @@ -214,3 +218,53 @@ int readline(int fd, char **buf, int *lenp) return 1; } + +/* Check if we should use the new caching interface + * This succeeds iff the "nfsd" filesystem is mounted on + * /proc/fs/nfs + */ +int +check_new_cache(void) +{ + struct stat stb; + return (stat("/proc/fs/nfs/filehandle", &stb) == 0); +} + + + + +/* flush the kNFSd caches. + * Set the flush time to the mtime of _PATH_ETAB or + * if force, to now. + * the caches to flush are: + * auth.unix.ip nfsd.export nfsd.fh + */ + +void +cache_flush(int force) +{ + struct stat stb; + int c; + char stime[20]; + char path[200]; + static char *cachelist[] = { + "auth.unix.ip", + "nfsd.export", + "nfsd.fh", + NULL + }; + stb.st_mtime = time(0); + if (!force) + stat(_PATH_ETAB, &stb); + + sprintf(stime, "%ld\n", stb.st_mtime); + for (c=0; cachelist[c]; c++) { + int fd; + sprintf(path, "/proc/net/rpc/%s/flush", cachelist[c]); + fd = open(path, O_RDWR); + if (fd) { + write(fd, stime, strlen(stime)); + close(fd); + } + } +} diff --git a/support/nfs/exports.c b/support/nfs/exports.c index 3d23959..3bef3ab 100644 --- a/support/nfs/exports.c +++ b/support/nfs/exports.c @@ -170,8 +170,10 @@ putexportent(struct exportent *ep) fprintf(fp, "%ssync,", (ep->e_flags & NFSEXP_ASYNC)? "a" : ""); fprintf(fp, "%swdelay,", (ep->e_flags & NFSEXP_GATHERED_WRITES)? "" : "no_"); - fprintf(fp, "%shide,", (ep->e_flags & NFSEXP_CROSSMNT)? + fprintf(fp, "%shide,", (ep->e_flags & NFSEXP_NOHIDE)? "no" : ""); + fprintf(fp, "%scrossmnt,", (ep->e_flags & NFSEXP_CROSSMNT)? + "" : "no"); fprintf(fp, "%ssecure,", (ep->e_flags & NFSEXP_INSECURE_PORT)? "in" : ""); fprintf(fp, "%sroot_squash,", (ep->e_flags & NFSEXP_ROOTSQUASH)? @@ -333,9 +335,9 @@ parseopts(char *cp, struct exportent *ep, int warn) had_sync_opt = 1; ep->e_flags |= NFSEXP_ASYNC; } else if (!strcmp(opt, "nohide")) - ep->e_flags |= NFSEXP_CROSSMNT; + ep->e_flags |= NFSEXP_NOHIDE; else if (!strcmp(opt, "hide")) - ep->e_flags &= ~NFSEXP_CROSSMNT; + ep->e_flags &= ~NFSEXP_NOHIDE; else if (!strcmp(opt, "crossmnt")) /* old style */ ep->e_flags |= NFSEXP_CROSSMNT; else if (!strcmp(opt, "nocrossmnt")) /* old style */ diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c index 60f130e..bbc4aad 100644 --- a/utils/exportfs/exportfs.c +++ b/utils/exportfs/exportfs.c @@ -42,12 +42,14 @@ main(int argc, char **argv) int f_reexport = 0; int f_ignore = 0; int i, c; + int new_cache=0; + int force_flush = 0; xlog_open("exportfs"); export_errno = 0; - while ((c = getopt(argc, argv, "aio:ruv")) != EOF) { + while ((c = getopt(argc, argv, "aio:ruvf")) != EOF) { switch(c) { case 'a': f_all = 1; @@ -68,6 +70,9 @@ main(int argc, char **argv) case 'v': f_verbose = 1; break; + case 'f': + force_flush = 1; + break; default: usage(); break; @@ -92,6 +97,8 @@ main(int argc, char **argv) return 0; } + new_cache = check_new_cache(); + if (f_export && ! f_ignore) export_read(_PATH_EXPORTS); if (f_export) { @@ -116,10 +123,15 @@ main(int argc, char **argv) unexportfs(argv[i], f_verbose); rmtab_read(); } - xtab_mount_read(); - exports_update(f_verbose); + if (!new_cache) { + xtab_mount_read(); + exports_update(f_verbose); + } xtab_export_write(); - xtab_mount_write(); + if (new_cache) + cache_flush(force_flush); + if (!new_cache) + xtab_mount_write(); return export_errno; } @@ -267,19 +279,21 @@ unexportfs(char *arg, int verbose) hname))) continue; if (verbose) { - if (exp->m_exported) { +#if 0 + if (exp->m_exported) printf("unexporting %s:%s from kernel\n", exp->m_client->m_hostname, exp->m_export.e_path); - } - else { + else +#endif printf("unexporting %s:%s\n", exp->m_client->m_hostname, exp->m_export.e_path); - } } +#if 0 if (exp->m_exported && !export_unexport(exp)) error(exp, errno); +#endif exp->m_xtabent = 0; exp->m_mayexport = 0; } @@ -333,8 +347,10 @@ dump(int verbose) c = dumpopt(c, "async"); if (ep->e_flags & NFSEXP_GATHERED_WRITES) c = dumpopt(c, "wdelay"); - if (ep->e_flags & NFSEXP_CROSSMNT) + if (ep->e_flags & NFSEXP_NOHIDE) c = dumpopt(c, "nohide"); + if (ep->e_flags & NFSEXP_CROSSMNT) + c = dumpopt(c, "crossmnt"); if (ep->e_flags & NFSEXP_INSECURE_PORT) c = dumpopt(c, "insecure"); if (ep->e_flags & NFSEXP_ROOTSQUASH) diff --git a/utils/mountd/Makefile b/utils/mountd/Makefile index 49b9900..34a2f4f 100644 --- a/utils/mountd/Makefile +++ b/utils/mountd/Makefile @@ -4,7 +4,7 @@ PROGRAM = mountd PREFIX = rpc. -OBJS = mountd.o mount_dispatch.o auth.o rmtab.o +OBJS = mountd.o mount_dispatch.o auth.o rmtab.o cache.o svc_run.o LIBDEPS = $(TOP)support/lib/libexport.a $(TOP)/support/lib/libnfs.a LIBS = -lexport -lnfs -lmisc $(LIBBSD) $(LIBWRAP) $(LIBNSL) MAN8 = mountd diff --git a/utils/mountd/auth.c b/utils/mountd/auth.c index d88c46f..ed867a7 100644 --- a/utils/mountd/auth.c +++ b/utils/mountd/auth.c @@ -31,16 +31,13 @@ enum auth_error }; static void auth_fixpath(char *path); -static nfs_export* auth_authenticate_internal - (char *what, struct sockaddr_in *caller, char *path, - struct hostent **hpp, enum auth_error *error); -static char *export_file = NULL; + +extern int new_cache; void auth_init(char *exports) { - export_file = exports; auth_reload(); xtab_mount_write(); } @@ -58,7 +55,6 @@ auth_reload() last_modified = stb.st_mtime; export_freeall(); - // export_read(export_file); xtab_export_read(); return 1; @@ -66,71 +62,67 @@ auth_reload() static nfs_export * auth_authenticate_internal(char *what, struct sockaddr_in *caller, - char *path, struct hostent **hpp, + char *path, struct hostent *hp, enum auth_error *error) { struct in_addr addr = caller->sin_addr; nfs_export *exp; - if (path[0] != '/') { - *error = bad_path; - return NULL; - } - auth_fixpath(path); - - if (!(*hpp = gethostbyaddr((const char *)&addr, sizeof(addr), AF_INET))) - *hpp = get_hostent((const char *)&addr, sizeof(addr), - AF_INET); - else { - /* must make sure the hostent is authorative. */ - char **sp; - struct hostent *forward = NULL; - char *tmpname; - - *hpp = hostent_dup (*hpp); - tmpname = xstrdup((*hpp)->h_name); - if (tmpname) { - forward = gethostbyname(tmpname); - free(tmpname); - } - if (forward) { - /* now make sure the "addr" is in the list */ - for (sp = forward->h_addr_list ; *sp ; sp++) { - if (memcmp(*sp, &addr, forward->h_length)==0) - break; - } - - if (!*sp) { - /* it was a FAKE */ - *error = faked_hostent; + static nfs_export my_exp; + static nfs_client my_client; + + if (new_cache) { + int i; + /* return static nfs_export with details filled in */ + if (my_client.m_naddr != 1 || + my_client.m_addrlist[0].s_addr != caller->sin_addr.s_addr) { + char *n; + my_client.m_naddr = 0; + my_client.m_addrlist[0] = caller->sin_addr; + n = client_compose(addr); + if (!n) return NULL; + strcpy(my_client.m_hostname, *n?n:"DEFAULT"); + free(n); + my_client.m_naddr = 1; + } + + my_exp.m_client = &my_client; + + exp = NULL; + for (i = 0; !exp && i < MCL_MAXTYPES; i++) + for (exp = exportlist[i]; exp; exp = exp->m_next) { + if (!client_member(my_client.m_hostname, exp->m_client->m_hostname)) + continue; + if (strcmp(path, exp->m_export.e_path)) + continue; + break; } - free (*hpp); - *hpp = hostent_dup (forward); + *error = not_exported; + if (!exp) + return exp; + + my_exp.m_export = exp->m_export; + exp = &my_exp; + } else { + + if (!(exp = export_find(hp, path))) { + *error = no_entry; + return NULL; } - else { - /* never heard of it. misconfigured DNS? */ - *error = no_forward_dns; + if (!exp->m_mayexport) { + *error = not_exported; return NULL; } - } - if (!(exp = export_find(*hpp, path))) { - *error = no_entry; - return NULL; - } - if (!exp->m_mayexport) { - *error = not_exported; - return NULL; - } + if (!(exp->m_export.e_flags & NFSEXP_INSECURE_PORT) && + (ntohs(caller->sin_port) < IPPORT_RESERVED/2 || + ntohs(caller->sin_port) >= IPPORT_RESERVED)) { + *error = illegal_port; + return NULL; + } - if (!(exp->m_export.e_flags & NFSEXP_INSECURE_PORT) && - (ntohs(caller->sin_port) < IPPORT_RESERVED/2 || - ntohs(caller->sin_port) >= IPPORT_RESERVED)) { - *error = illegal_port; - return NULL; } - *error = success; return exp; @@ -154,15 +146,19 @@ auth_authenticate(char *what, struct sockaddr_in *caller, char *path) strncpy(epath, path, sizeof (epath) - 1); epath[sizeof (epath) - 1] = '\0'; - + auth_fixpath(epath);/* strip dup '/' etc */ + + hp = get_reliable_hostbyaddr((const char*)&caller->sin_addr, sizeof(struct in_addr), + AF_INET); + if (!hp) + hp = get_hostent((const char*)&caller->sin_addr, sizeof(struct in_addr), + AF_INET); + if (!hp) + return exp; /* Try the longest matching exported pathname. */ while (1) { - if (hp) { - free (hp); - hp = NULL; - } exp = auth_authenticate_internal(what, caller, epath, - &hp, &error); + hp, &error); if (exp || (error != not_exported && error != no_entry)) break; /* We have to treat the root, "/", specially. */ @@ -171,6 +167,7 @@ auth_authenticate(char *what, struct sockaddr_in *caller, char *path) if (p == epath) p++; *p = '\0'; } + free(hp); switch (error) { case bad_path: @@ -223,6 +220,9 @@ auth_authenticate(char *what, struct sockaddr_in *caller, char *path) return exp; } +/* + * Remove duplicate and trailing '/' (Except for leading slash) + */ static void auth_fixpath(char *path) { diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c new file mode 100644 index 0000000..1d1567d --- /dev/null +++ b/utils/mountd/cache.c @@ -0,0 +1,358 @@ + +/* + * Handle communication with knfsd internal cache + * + * We open /proc/net/rpc/{auth.unix.ip,nfsd.export,nfsd.fh}/channel + * and listen for requests (using my_svc_run) + * + */ +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "misc.h" +#include "nfslib.h" +#include "exportfs.h" +#include "mountd.h" +#include "xmalloc.h" + +/* + * Support routines for text-based upcalls. + * Fields are separated by spaces. + * Fields are either mangled to quote space tab newline slosh with slosh + * or a hexified with a leading \x + * Record is terminated with newline. + * + */ +void cache_export_ent(char *domain, struct exportent *exp); + + +char *lbuf = NULL; +int lbuflen = 0; + +void auth_unix_ip(FILE *f) +{ + /* requests are + * class IP-ADDR + * Ignore if class != "nfsd" + * Otherwise find domainname and write back: + * + * "nfsd" IP-ADDR expiry domainname + */ + char *cp; + char class[20]; + char ipaddr[20]; + char *client; + struct in_addr addr; + if (readline(fileno(f), &lbuf, &lbuflen) != 1) + return; + + cp = lbuf; + + if (qword_get(&cp, class, 20) <= 0 || + strcmp(class, "nfsd") != 0) + return; + + if (qword_get(&cp, ipaddr, 20) <= 0) + return; + + if (inet_aton(ipaddr, &addr)==0) + return; + + /* addr is a valid, interesting address, find the domain name... */ + client = client_compose(addr); + + + qword_print(f, "nfsd"); + qword_print(f, ipaddr); + qword_printint(f, time(0)+30*60); + if (client) + qword_print(f, *client?client:"DEFAULT"); + qword_eol(f); + + if (client) free(client); + +} + +void nfsd_fh(FILE *f) +{ + /* request are: + * domain fsidtype fsid + * interpret fsid, find export point and options, and write: + * domain fsidtype fsid expiry path + */ + char *cp; + char *dom; + int fsidtype; + int fsidlen; + unsigned int dev, major=0, minor=0; + unsigned int inode=0; + unsigned int fsidnum=0; + char fsid[32]; + struct exportent *found = NULL; + nfs_export *exp; + int i; + + if (readline(fileno(f), &lbuf, &lbuflen) != 1) + return; + + cp = lbuf; + + dom = malloc(strlen(cp)); + if (dom == NULL) + return; + if (qword_get(&cp, dom, strlen(cp)) <= 0) + goto out; + if (qword_get_int(&cp, &fsidtype) != 0) + goto out; + if (fsidtype < 0 || fsidtype > 1) + goto out; /* unknown type */ + if ((fsidlen = qword_get(&cp, fsid, 32)) <= 0) + goto out; + switch(fsidtype) { + case 0: /* 4 bytes: 2 major, 2 minor, 4 inode */ + if (fsidlen != 8) + goto out; + memcpy(&dev, fsid, 4); + memcpy(&inode, fsid+4, 4); + major = ntohl(dev)>>16; + minor = ntohl(dev) & 0xFFFF; + break; + + case 1: /* 4 bytes - fsid */ + if (fsidlen != 4) + goto out; + memcpy(&fsidnum, fsid, 4); + break; + } + + /* Now determine export point for this fsid/domain */ + for (i=0 ; i < MCL_MAXTYPES; i++) { + for (exp = exportlist[i]; exp; exp = exp->m_next) { + if (!client_member(dom, exp->m_client->m_hostname)) + continue; + if (fsidtype == 1 && + ((exp->m_export.e_flags & NFSEXP_FSID) == 0 || + exp->m_export.e_fsid != fsidnum)) + continue; + if (fsidtype == 0) { + struct stat stb; + if (stat(exp->m_export.e_path, &stb) != 0) + continue; + if (stb.st_ino != inode) + continue; + if (major != major(stb.st_dev) || + minor != minor(stb.st_dev)) + continue; + } + /* It's a match !! */ + if (!found) + found = &exp->m_export; + else if (strcmp(found->e_path, exp->m_export.e_path)!= 0) + { + xlog(L_WARNING, "%s and %s have name filehandle for %s, using first", + found->e_path, exp->m_export.e_path, dom); + } + } + } + cache_export_ent(dom, found); + + qword_print(f, dom); + qword_printint(f, fsidtype); + qword_printhex(f, fsid, fsidlen); + qword_printint(f, time(0)+30*60); + if (found) + qword_print(f, found->e_path); + qword_eol(f); + out: + free(dom); + return; +} + +void nfsd_export(FILE *f) +{ + /* requests are: + * domain path + * determine export options and return: + * domain path expiry flags anonuid anongid fsid + */ + + char *cp; + int i; + char *dom, *path; + nfs_export *exp, *found = NULL; + + + if (readline(fileno(f), &lbuf, &lbuflen) != 1) + return; + + cp = lbuf; + dom = malloc(strlen(cp)); + path = malloc(strlen(cp)); + + if (!dom || !path) + goto out; + + if (qword_get(&cp, dom, strlen(lbuf)) <= 0) + goto out; + if (qword_get(&cp, path, strlen(lbuf)) <= 0) + goto out; + + /* now find flags for this export point in this domain */ + for (i=0 ; i < MCL_MAXTYPES; i++) { + for (exp = exportlist[i]; exp; exp = exp->m_next) { + if (!client_member(dom, exp->m_client->m_hostname)) + continue; + if (strcmp(path, exp->m_export.e_path)) + continue; + if (!found) + found = exp; + else { + xlog(L_WARNING, "%s exported to both %s and %s in %s", + path, exp->m_client->m_hostname, found->m_client->m_hostname, + dom); + } + } + } + + qword_print(f, dom); + qword_print(f, path); + qword_printint(f, time(0)+30*60); + if (found) { + qword_printint(f, found->m_export.e_flags); + qword_printint(f, found->m_export.e_anonuid); + qword_printint(f, found->m_export.e_anongid); + qword_printint(f, found->m_export.e_fsid); + } + qword_eol(f); + out: + if (dom) free(dom); + if (path) free(path); +} + + +struct { + char *cache_name; + void (*cache_handle)(FILE *f); + FILE *f; +} cachelist[] = { + { "auth.unix.ip", auth_unix_ip}, + { "nfsd.export", nfsd_export}, + { "nfsd.fh", nfsd_fh}, + { NULL, NULL } +}; + +void cache_open(void) +{ + int i; + for (i=0; cachelist[i].cache_name; i++ ){ + char path[100]; + sprintf(path, "/proc/net/rpc/%s/channel", cachelist[i].cache_name); + cachelist[i].f = fopen(path, "r+"); + } +} + +void cache_set_fds(fd_set *fdset) +{ + int i; + for (i=0; cachelist[i].cache_name; i++) { + if (cachelist[i].f) + FD_SET(fileno(cachelist[i].f), fdset); + } +} + +int cache_process_req(fd_set *readfds) +{ + int i; + int cnt = 0; + for (i=0; cachelist[i].cache_name; i++) { + if (cachelist[i].f != NULL && + FD_ISSET(fileno(cachelist[i].f), readfds)) { + cnt++; + cachelist[i].cache_handle(cachelist[i].f); + } + } + return cnt; +} + + +/* + * Give IP->domain and domain+path->options to kernel + * % echo nfsd $IP $[now+30*60] $domain > /proc/net/rpc/auth.unix.ip/channel + * % echo $domain $path $[now+30*60] $options $anonuid $anongid $fsid > /proc/net/rpc/nfsd.export/channel + */ + +void cache_export_ent(char *domain, struct exportent *exp) +{ + + FILE *f = fopen("/proc/net/rpc/nfsd.export/channel", "r+"); + if (!f) + return; + + qword_print(f, domain); + qword_print(f, exp->e_path); + qword_printint(f, time(0)+30*60); + qword_printint(f, exp->e_flags); + qword_printint(f, exp->e_anonuid); + qword_printint(f, exp->e_anongid); + qword_printint(f, exp->e_fsid); + qword_eol(f); + + fclose(f); +} + +void cache_export(nfs_export *exp) +{ + FILE *f; + + f = fopen("/proc/net/rpc/auth.unix.ip/channel", "r+"); + if (!f) + return; + + qword_print(f, "nfsd"); + qword_print(f, inet_ntoa(exp->m_client->m_addrlist[0])); + qword_printint(f, time(0)+30*60); + qword_print(f, exp->m_client->m_hostname); + qword_eol(f); + + fclose(f); + + cache_export_ent(exp->m_client->m_hostname, &exp->m_export); +} + +/* Get a filehandle. + * { + * echo $domain $path $length + * read filehandle <&0 + * } <> /proc/fs/nfs/filehandle + */ +struct nfs_fh_len * +cache_get_filehandle(nfs_export *exp, int len) +{ + FILE *f = fopen("/proc/fs/nfs/filehandle", "r+"); + char buf[200]; + char *bp = buf; + static struct nfs_fh_len fh; + if (!f) + return NULL; + + qword_print(f, exp->m_client->m_hostname); + qword_print(f, exp->m_export.e_path); + qword_printint(f, len); + qword_eol(f); + + if (fgets(buf, sizeof(buf), f) == NULL) + return NULL; + memset(fh.fh_handle, 0, sizeof(fh.fh_handle)); + fh.fh_size = qword_get(&bp, fh.fh_handle, NFS3_FHSIZE); + return &fh; +} + diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c index 971e4f4..8158ffd 100644 --- a/utils/mountd/mountd.c +++ b/utils/mountd/mountd.c @@ -24,10 +24,19 @@ #include "mountd.h" #include "rpcmisc.h" +extern void cache_open(void); +extern struct nfs_fh_len *cache_get_filehandle(nfs_export *exp, int len); +extern void cache_export(nfs_export *exp); + +extern void my_svc_run(void); + + static void usage(const char *, int exitcode); static exports get_exportlist(void); static struct nfs_fh_len *get_rootfh(struct svc_req *, dirpath *, int *, int v3); +int new_cache; + static struct option longopts[] = { { "foreground", 0, 0, 'F' }, @@ -182,7 +191,7 @@ mount_pathconf_2_svc(struct svc_req *rqstp, dirpath *path, ppathcnf *res) } /* Now authenticate the intruder... */ - if (!(exp = auth_authenticate("mount", sin, p))) { + if (!(exp = auth_authenticate("pathconf", sin, p))) { return 1; } else if (stat(p, &stb) < 0) { xlog(L_WARNING, "can't stat exported dir %s: %s", @@ -264,6 +273,20 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, int *error, int v3) } else if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) { xlog(L_WARNING, "%s is not a directory or regular file", p); *error = NFSERR_NOTDIR; + } else if (new_cache) { + /* This will be a static private nfs_export with just one + * address. We feed it to kernel then extract the filehandle, + * + */ + struct nfs_fh_len *fh; + + cache_export(exp); + fh = cache_get_filehandle(exp, v3?64:32); + if (fh == NULL) + *error = NFSERR_ACCES; + else + *error = NFS_OK; + return fh; } else { struct nfs_fh_len *fh; @@ -491,6 +514,10 @@ main(int argc, char **argv) (void) close(fd); } + new_cache = check_new_cache(); + if (new_cache) + cache_open(); + if (nfs_version & 0x1) rpc_init("mountd", MOUNTPROG, MOUNTVERS, mount_dispatch, port); @@ -529,7 +556,7 @@ main(int argc, char **argv) xlog_background(); } - svc_run(); + my_svc_run(); xlog(L_ERROR, "Ack! Gack! svc_run returned!\n"); exit(1); diff --git a/utils/mountd/rmtab.c b/utils/mountd/rmtab.c index 869b5f8..8c4a5a9 100644 --- a/utils/mountd/rmtab.c +++ b/utils/mountd/rmtab.c @@ -32,7 +32,7 @@ slink_safe_rename(const char * oldpath, const char * newpath) int r; struct stat s; char slink_path[PATH_MAX]; - char real_newpath = newpath; + const char *real_newpath = newpath; if ((lstat(newpath, &s) == 0) && S_ISLNK(s.st_mode)) { /* New path is a symbolic link, do not destroy but follow */ diff --git a/utils/mountd/svc_run.c b/utils/mountd/svc_run.c new file mode 100644 index 0000000..a1ef74a --- /dev/null +++ b/utils/mountd/svc_run.c @@ -0,0 +1,89 @@ +/* + * Copyright (C) 1984 Sun Microsystems, Inc. + * Based on svc_run.c from statd which claimed: + * Modified by Jeffrey A. Uphoff, 1995, 1997-1999. + * Modified by Olaf Kirch, 1996. + * + */ + +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * Allow svc_run to listen to other file descriptors as well + */ + +/* + * This is the RPC server side idle loop. + * Wait for input, call server program. + */ +#include "config.h" +#include +#include +#include "xlog.h" +#include +#include + +void cache_set_fds(fd_set *fdset); +int cache_process_req(fd_set *readfds); + + +/* + * The heart of the server. A crib from libc for the most part... + */ +void +my_svc_run(void) +{ + fd_set readfds; + int selret; + + for (;;) { + + readfds = svc_fdset; + cache_set_fds(&readfds); + + selret = select(FD_SETSIZE, &readfds, + (void *) 0, (void *) 0, (struct timeval *) 0); + + + switch (selret) { + case -1: + if (errno == EINTR || errno == ECONNREFUSED + || errno == ENETUNREACH || errno == EHOSTUNREACH) + continue; + xlog(L_ERROR, "my_svc_run() - select: %m"); + return; + + default: + selret -= cache_process_req(&readfds); + if (selret) + svc_getreqset(&readfds); + } + } +} -- 2.39.2