diff -up nfs-utils-1.2.1/support/export/xtab.c.orig nfs-utils-1.2.1/support/export/xtab.c --- nfs-utils-1.2.1/support/export/xtab.c.orig 2009-11-04 06:13:56.000000000 -0500 +++ nfs-utils-1.2.1/support/export/xtab.c 2009-11-11 14:01:56.370963000 -0500 @@ -19,7 +19,9 @@ #include "exportfs.h" #include "xio.h" #include "xlog.h" +#include "v4root.h" +int v4root_needed; static void cond_rename(char *newfile, char *oldfile); static int @@ -36,6 +38,8 @@ xtab_read(char *xtab, char *lockfn, int if ((lockid = xflock(lockfn, "r")) < 0) return 0; setexportent(xtab, "r"); + if (is_export == 1) + v4root_needed = 1; while ((xp = getexportent(is_export==0, 0)) != NULL) { if (!(exp = export_lookup(xp->e_hostname, xp->e_path, is_export != 1)) && !(exp = export_create(xp, is_export!=1))) { @@ -48,6 +52,8 @@ xtab_read(char *xtab, char *lockfn, int case 1: exp->m_xtabent = 1; exp->m_mayexport = 1; + if ((xp->e_flags & NFSEXP_FSID) && xp->e_fsid == 0) + v4root_needed = 0; break; case 2: exp->m_exported = -1;/* may be exported */ diff -up nfs-utils-1.2.1/support/include/exportfs.h.orig nfs-utils-1.2.1/support/include/exportfs.h --- nfs-utils-1.2.1/support/include/exportfs.h.orig 2009-11-04 06:13:56.000000000 -0500 +++ nfs-utils-1.2.1/support/include/exportfs.h 2009-11-11 14:01:56.377960000 -0500 @@ -12,6 +12,17 @@ #include #include "nfslib.h" +enum nfsd_fsid { + FSID_DEV = 0, + FSID_NUM, + FSID_MAJOR_MINOR, + FSID_ENCODE_DEV, + FSID_UUID4_INUM, + FSID_UUID8, + FSID_UUID16, + FSID_UUID16_INUM, +}; + enum { MCL_FQDN = 0, MCL_SUBNETWORK, diff -up nfs-utils-1.2.1/support/include/nfs/export.h.orig nfs-utils-1.2.1/support/include/nfs/export.h --- nfs-utils-1.2.1/support/include/nfs/export.h.orig 2009-11-04 06:13:56.000000000 -0500 +++ nfs-utils-1.2.1/support/include/nfs/export.h 2009-11-11 14:01:56.383967000 -0500 @@ -24,6 +24,7 @@ #define NFSEXP_FSID 0x2000 #define NFSEXP_CROSSMOUNT 0x4000 #define NFSEXP_NOACL 0x8000 /* reserved for possible ACL related use */ -#define NFSEXP_ALLFLAGS 0xFFFF +#define NFSEXP_V4ROOT 0x10000 +#define NFSEXP_ALLFLAGS 0x1FFFF #endif /* _NSF_EXPORT_H */ diff -up nfs-utils-1.2.1/support/include/nfslib.h.orig nfs-utils-1.2.1/support/include/nfslib.h --- nfs-utils-1.2.1/support/include/nfslib.h.orig 2009-11-04 06:13:56.000000000 -0500 +++ nfs-utils-1.2.1/support/include/nfslib.h 2009-11-11 14:01:56.390963000 -0500 @@ -88,6 +88,7 @@ struct exportent { int e_fslocmethod; char * e_fslocdata; char * e_uuid; + void * e_v4root; struct sec_entry e_secinfo[SECFLAVOR_COUNT+1]; }; diff -up /dev/null nfs-utils-1.2.1/support/include/v4root.h --- /dev/null 2009-11-11 11:00:17.075766506 -0500 +++ nfs-utils-1.2.1/support/include/v4root.h 2009-11-11 14:01:56.399965000 -0500 @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2009 Red Hat + * support/include/v4root.h + * + * Support routines for dynamic pseudo roots. + * + */ + +#ifndef V4ROOT_H +#define V4ROOT_H + +extern int v4root_needed; + +extern struct exportent *v4root_chkroot(int , unsigned int , char *); +extern struct exportent *v4root_export(char *, int); +extern struct exportent *v4root_lookup(char *, nfs_export *); +extern void v4root_free(struct exportent *); +extern void v4root_unset(void), v4root_set(void); + +#endif /* V4ROOT_H */ diff -up nfs-utils-1.2.1/utils/mountd/auth.c.orig nfs-utils-1.2.1/utils/mountd/auth.c --- nfs-utils-1.2.1/utils/mountd/auth.c.orig 2009-11-04 06:13:56.000000000 -0500 +++ nfs-utils-1.2.1/utils/mountd/auth.c 2009-11-11 14:01:56.405963000 -0500 @@ -20,6 +20,7 @@ #include "exportfs.h" #include "mountd.h" #include "xmalloc.h" +#include "v4root.h" enum auth_error { @@ -98,10 +99,13 @@ auth_reload() last_inode = stb.st_ino; } + v4root_unset(); export_freeall(); memset(&my_client, 0, sizeof(my_client)); xtab_export_read(); check_useipaddr(); + v4root_set(); + ++counter; return counter; diff -up nfs-utils-1.2.1/utils/mountd/cache.c.orig nfs-utils-1.2.1/utils/mountd/cache.c --- nfs-utils-1.2.1/utils/mountd/cache.c.orig 2009-11-04 06:13:56.000000000 -0500 +++ nfs-utils-1.2.1/utils/mountd/cache.c 2009-11-11 14:01:56.414960000 -0500 @@ -32,23 +32,12 @@ #include "xmalloc.h" #include "fsloc.h" #include "pseudoflavors.h" +#include "v4root.h" #ifdef USE_BLKID #include "blkid/blkid.h" #endif - -enum nfsd_fsid { - FSID_DEV = 0, - FSID_NUM, - FSID_MAJOR_MINOR, - FSID_ENCODE_DEV, - FSID_UUID4_INUM, - FSID_UUID8, - FSID_UUID16, - FSID_UUID16_INUM, -}; - /* * Support routines for text-based upcalls. * Fields are separated by spaces. @@ -135,6 +124,8 @@ void auth_unix_gid(FILE *f) if (readline(fileno(f), &lbuf, &lbuflen) != 1) return; + xlog(D_CALL, "auth_unix_gid: '%s'", lbuf); + cp = lbuf; if (qword_get_int(&cp, &uid) != 0) return; @@ -391,6 +382,12 @@ void nfsd_fh(FILE *f) auth_reload(); + /* Check to see if the kenel is looking for the pseudo root */ + if ((found = v4root_chkroot(fsidtype, fsidnum, fhuuid))) { + found_path = strdup(found->e_path); + goto found; + } + /* Now determine export point for this fsid/domain */ for (i=0 ; i < MCL_MAXTYPES; i++) { nfs_export *next_exp; @@ -511,7 +508,23 @@ void nfsd_fh(FILE *f) */ goto out; } + if (!found) { + /* + * See if this is a pesudo export + */ + switch(fsidtype) { + case FSID_UUID4_INUM: + case FSID_UUID8: + case FSID_UUID16: + case FSID_UUID16_INUM: + found = v4root_export(fhuuid, uuidlen); + break; + } + if (found) + found_path = strdup(found->e_path); + } +found: if (found) if (cache_export_ent(dom, found, found_path) < 0) found = 0; @@ -629,6 +642,7 @@ void nfsd_export(FILE *f) int found_type = 0; struct in_addr addr; struct hostent *he = NULL; + struct exportent *v4root = NULL; if (readline(fileno(f), &lbuf, &lbuflen) != 1) @@ -663,10 +677,18 @@ void nfsd_export(FILE *f) path[l] == '/' && is_mountpoint(path))) /* ok */; - else + else { + /* See if the path is part of the psuedo root */ + if (v4root_needed && !v4root) + v4root = v4root_lookup(path, exp); continue; - } else if (strcmp(path, exp->m_export.e_path) != 0) + } + } else if (strcmp(path, exp->m_export.e_path) != 0) { + /* See if the path is part of the psuedo root */ + if (v4root_needed && !v4root) + v4root = v4root_lookup(path, exp); continue; + } if (use_ipaddr) { if (he == NULL) { if (!inet_aton(dom, &addr)) @@ -705,17 +727,28 @@ void nfsd_export(FILE *f) } if (found) { + xlog(D_CALL, "nfsd_export: found: path %s", path); if (dump_to_cache(f, dom, path, &found->m_export) < 0) { xlog(L_WARNING, "Cannot export %s, possibly unsupported filesystem" " or fsid= required", path); dump_to_cache(f, dom, path, NULL); } - } else { + } else if (v4root) { + xlog(D_CALL, "nfsd_export: vroot: path %s", path); + dump_to_cache(f, dom, path, v4root); + found = (nfs_export *)v4root; + } else { dump_to_cache(f, dom, path, NULL); } out: - xlog(D_CALL, "nfsd_export: found %p path %s", found, path ? path : NULL); + /* + * If a psuedo export was create and its not needed + * free it up. + */ + if (v4root && found != (nfs_export *)v4root) + v4root_free(v4root); + if (dom) free(dom); if (path) free(path); if (he) free(he); @@ -743,7 +776,9 @@ void cache_open(void) if (!manage_gids && cachelist[i].cache_handle == auth_unix_gid) continue; sprintf(path, "/proc/net/rpc/%s/channel", cachelist[i].cache_name); - cachelist[i].f = fopen(path, "r+"); + if ((cachelist[i].f = fopen(path, "r+")) == NULL) + xlog(L_ERROR, "cache_open: Unable to open '%s': errno %d (%s)", + path, errno, strerror(errno)); } } diff -up nfs-utils-1.2.1/utils/mountd/Makefile.am.orig nfs-utils-1.2.1/utils/mountd/Makefile.am --- nfs-utils-1.2.1/utils/mountd/Makefile.am.orig 2009-11-04 06:13:56.000000000 -0500 +++ nfs-utils-1.2.1/utils/mountd/Makefile.am 2009-11-11 14:01:56.421960000 -0500 @@ -8,7 +8,7 @@ KPREFIX = @kprefix@ sbin_PROGRAMS = mountd mountd_SOURCES = mountd.c mount_dispatch.c auth.c rmtab.c cache.c \ - svc_run.c fsloc.c mountd.h + svc_run.c fsloc.c v4root.c mountd.h mountd_LDADD = ../../support/export/libexport.a \ ../../support/nfs/libnfs.a \ ../../support/misc/libmisc.a \ diff -up /dev/null nfs-utils-1.2.1/utils/mountd/v4root.c --- /dev/null 2009-11-11 11:00:17.075766506 -0500 +++ nfs-utils-1.2.1/utils/mountd/v4root.c 2009-11-11 14:02:39.699100000 -0500 @@ -0,0 +1,432 @@ +/* + * Copyright (C) 2009 Red Hat + * + * support/export/v4root.c + * + * Routines used to support NFSv4 pseudo roots + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "xlog.h" +#include "exportfs.h" +#include "nfslib.h" +#include "misc.h" +#include "v4root.h" + +#ifndef _PATH_PSEUDO_ROOT +#define _PATH_PSEUDO_ROOT "/" +#endif + +#ifndef _PSEUDO_ROOT_FSID +#define _PSEUDO_ROOT_FSID 0 +#endif + +extern int get_uuid(char *path, char *uuid, int uuidlen, char *u); + +#define HASH_TABLE_SIZE 1021 +typedef struct _hash_head { + TAILQ_HEAD(export_list, _exports_t) h_head; +} hash_head; +hash_head exports_tbl[HASH_TABLE_SIZE]; + +typedef struct _exports_t { + TAILQ_ENTRY(_exports_t) list; + char *path; + hash_head *head; + char uuid_len; + char uuid[sizeof(uuid_t)]; + struct exportent p_export; +} exports_t; + + + +static exports_t *hash_export_lookup(char *, unsigned int); +static void hash_export_add(struct _exports_t *, int); +static void hash_mount_free(void); + +static inline unsigned int strtoint(char *str, int len) +{ + unsigned int n = 0; + int i; + + for (i=0; i < len; i++) + n+=((int)str[i])*i; + return n; +} +static inline int hashint(unsigned int num) +{ + return num % HASH_TABLE_SIZE; +} +#define HASH(_s, _l) hashint(strtoint((_s), (_l))) +void v4root_set(void); +void v4root_unset(void); +static int v4root_support(void); + +static struct exportent *v4root_create(char *, nfs_export *); + +int v4root_needed; + +static nfs_export pr_export = { + .m_next = NULL, + .m_client = NULL, + .m_export = { + .e_hostname = "*", + .e_path = _PATH_PSEUDO_ROOT, + .e_flags = NFSEXP_READONLY | NFSEXP_ROOTSQUASH + | NFSEXP_NOSUBTREECHECK | NFSEXP_FSID + | NFSEXP_CROSSMOUNT | NFSEXP_V4ROOT, + .e_anonuid = 65534, + .e_anongid = 65534, + .e_squids = NULL, + .e_nsquids = 0, + .e_sqgids = NULL, + .e_nsqgids = 0, + .e_fsid = 0, + .e_mountpoint = NULL, + }, + .m_exported = 0, + .m_xtabent = 1, + .m_mayexport = 1, + .m_changed = 0, + .m_warned = 0, +}; +static nfs_export *pseudo_root; + +/* + * Return the number '/' in the path + */ +inline static int slash_count(char *path) +{ + int i, slashs=0; + + for (i=0; i < strlen(path); i++) { + if (path[i] == '/') + slashs++; + } + return slashs; +} +/* + * Make sure the kernel has pseudo root support. + */ +static int +v4root_support() +{ + static int kernel_support = -1; + char *ptr, version[64]; + int major, minor; + FILE *fp; + + if (kernel_support != -1) + return kernel_support; + + kernel_support = 0; + fp = fopen("/proc/fs/nfsd/exports", "r"); + if (fp == NULL) + goto out; + + ptr = fgets(version, 64, fp); + fclose(fp); + if (ptr == NULL) + goto out; + + while(*ptr && isdigit(*ptr) == 0) + ptr++; + if (*ptr == '\0') + goto out; + + major = minor = 0; + sscanf(ptr, " %d.%d",&major, &minor); + if (major >= 1 && minor >= 2) + kernel_support = 1; +out: + if (!kernel_support) { + xlog(L_WARNING, "Kernel does not have pseudo root support."); + xlog(L_WARNING, "NFS v4 mounts will be disabled unless fsid=0"); + xlog(L_WARNING, "is specfied in /etc/exports file."); + } + + return kernel_support; +} +/* + * Build a table of pseudo exports by running through + * the real export looking at the components of the path + * that make up the export. Those path components, if + * not exported, will become pseudo exports allowing them + * to be found when the kernel does an upcall looking for + * components of the v4 mount. + */ +void +v4root_set() +{ + nfs_export *exp, *nxt; + struct exportent *proot; + int i, insecure = 0; + char *path, *ptr; + char *hostname; + + if (!v4root_needed) + return; + + if (!v4root_support()) + return; + + pseudo_root = &pr_export; + if ((proot = v4root_create(_PATH_PSEUDO_ROOT, pseudo_root)) == NULL) { + xlog(L_WARNING, "v4root_set: Unable to create" + "pseudo export for '%s'", _PATH_PSEUDO_ROOT); + pseudo_root = NULL; + return; + } + + for (i = 0; i < MCL_MAXTYPES; i++) { + for (exp = exportlist[i].p_head; exp; exp = nxt) { + nxt = exp->m_next; + hostname = exp->m_export.e_hostname; + + path = strdup(exp->m_export.e_path); + ptr = path + 1; + while ((ptr = strchr(ptr, '/')) != NULL) { + *ptr = '\0'; + if (export_lookup(hostname, path, 0) == NULL) + if (v4root_create(path, exp) == NULL) { + xlog(L_WARNING, "v4root_set: Unable to create" + "pseudo export for '%s'", path); + break; + } + *ptr = '/'; + ptr++; + } + /* Make note of insecure exports */ + if (!insecure) + insecure = (exp->m_export.e_flags & NFSEXP_INSECURE_PORT); + + free(path); + } + } + /* + * If there are any insecure exports, the pseudo root + * also has to be insecure + */ + if (insecure) { + pseudo_root->m_export.e_flags |= NFSEXP_INSECURE_PORT; + proot->e_flags |= NFSEXP_INSECURE_PORT; + } +} + +/* + * Unset the pseudo root export + */ +void +v4root_unset() +{ + pseudo_root = NULL; + hash_mount_free(); +} + +/* + * The kernel will do an upcall looking for the pseudo + * root via its fsid. When the wanted fsid equals + * PSEUDO_ROOT_FSID return the pseudo root export. + */ +struct exportent * +v4root_chkroot(int fsidtype, unsigned int fsidnum, char *fhuuid) +{ + if (pseudo_root == NULL) + return NULL; + + if (fsidtype != FSID_NUM) + return NULL; + + if (fsidnum != _PSEUDO_ROOT_FSID) + return NULL; + + return &pseudo_root->m_export; +} + +/* + * Create a pseudo export, if one does not + * already exist. + */ +static struct exportent * +v4root_create(char *path, nfs_export *exp) +{ + static struct exportent *p_export = NULL; + exports_t *pexp; + char uuid_len = sizeof(uuid_t); + char uuid[sizeof(uuid_t)]; + + if (pseudo_root == NULL) + return NULL; + + /* Check to see if the export already exists */ + get_uuid(path, NULL, uuid_len, uuid); + if ((p_export = v4root_export(uuid, uuid_len)) != NULL) + return p_export; + + pexp = (exports_t *)malloc(sizeof(exports_t)); + if (pexp == NULL) { + xlog(L_WARNING, "v4root_create: No memory for pseudo export"); + return NULL; + } + p_export = &pexp->p_export; + pexp->path = strdup(path); + if (pexp->path == 0) { + xlog(L_WARNING, "v4root_create: No memory for pseudo path"); + free(pexp); + return NULL; + } + pexp->uuid_len = uuid_len; + memcpy(pexp->uuid, uuid, uuid_len); + + dupexportent(&pexp->p_export, &pr_export.m_export); + strcpy(p_export->e_path, path); + p_export->e_flags &= ~NFSEXP_FSID; + p_export->e_v4root = (void *)pexp; + + hash_export_add(pexp, HASH(pexp->uuid, sizeof(uuid_t))); + + xlog(D_CALL, "v4root_create: path '%s'", p_export->e_path); + + return p_export; +} + +/* + * See if the pseudo export exists + */ +struct exportent * +v4root_lookup(char *path, nfs_export *exp) +{ + static struct exportent *p_export = NULL; + char *epath = exp->m_export.e_path; + int elen, plen; + char uuid_len = sizeof(uuid_t); + char uuid[sizeof(uuid_t)]; + + if (pseudo_root == NULL) + return NULL; + + /* Path needs to be a subset of e_path */ + elen = strlen(epath); + plen = strlen(path); + if (plen >= elen) + return NULL; + + if (memcmp(path, epath, plen) != 0) + return NULL; + + /* Now to see if the export exists */ + get_uuid(path, NULL, uuid_len, uuid); + p_export = v4root_export(uuid, uuid_len); + + return p_export; +} + +/* + * Free a pseudo export + */ +void +v4root_free(struct exportent *p_export) +{ + exports_t *pexp = (exports_t *)p_export->e_v4root; + hash_head *head = (hash_head *)pexp->head; + + free(pexp->path); + TAILQ_REMOVE(&head->h_head, pexp, list); +} + +/* + * Return a pseudo export that match the given uuid + */ +struct exportent * +v4root_export(char *fhuuid, int uuidlen) +{ + struct exportent *p_export = NULL; + exports_t *pexp; + int len = MIN(uuidlen, sizeof(uuid_t)); + + if (pseudo_root == NULL) + return NULL; + + pexp = hash_export_lookup(fhuuid, len); + if (pexp) { + p_export = &pexp->p_export; + xlog(D_CALL, "v4root_export: path %s", p_export->e_path); + } + return p_export; +} + +/* + * Add pseudo export to export table + */ +static void hash_export_add(struct _exports_t *exp, int hash) +{ + hash_head *head; + + head = &(exports_tbl[hash]); + exp->head = head; + + if (TAILQ_EMPTY(&head->h_head)) + TAILQ_INSERT_HEAD(&head->h_head, exp, list); + else + TAILQ_INSERT_TAIL(&head->h_head, exp, list); +} + +/* + * Lookup a pseudo export using the uuid and inode number + */ +static exports_t * +hash_export_lookup(char *uuid, unsigned int uuidlen) +{ + exports_t *pexp; + hash_head *head; + int hash = HASH(uuid, uuidlen); + + head = &(exports_tbl[hash]); + + TAILQ_FOREACH(pexp, &head->h_head, list) { + if (memcmp(pexp->uuid, uuid, uuidlen) == 0) + return pexp; + } + return NULL; + +} + +/* + * Free up pseudo export table + */ +static void hash_mount_free() +{ + hash_head *head; + exports_t *e1, *e2; + int hash; + + for (hash=0; hash < HASH_TABLE_SIZE; hash++) { + head = &(exports_tbl[hash]); + if (head == NULL) + continue; + e1 = TAILQ_FIRST(&head->h_head); + while (e1 != NULL) { + free(e1->path); + e2 = TAILQ_NEXT(e1, list); + TAILQ_REMOVE(&head->h_head, e1, list); + free(e1); + e1 = e2; + } + TAILQ_INIT(&head->h_head); + } +}