diff --git a/.cvsignore b/.cvsignore index 3b0ffd9..79b6b91 100644 --- a/.cvsignore +++ b/.cvsignore @@ -1,3 +1,4 @@ nfs.doc.tar.gz nfs-utils-1.1.6.tar.bz2 nfs-utils-1.2.0.tar.bz2 +nfs-utils-1.2.1.tar.bz2 diff --git a/nfs-utils-1.2.0-v4root-rel7.patch b/nfs-utils-1.2.0-v4root-rel7.patch deleted file mode 100644 index dc010a2..0000000 --- a/nfs-utils-1.2.0-v4root-rel7.patch +++ /dev/null @@ -1,699 +0,0 @@ -diff -up nfs-utils-1.2.0/support/export/xtab.c.save nfs-utils-1.2.0/support/export/xtab.c ---- nfs-utils-1.2.0/support/export/xtab.c.save 2009-10-20 08:46:50.000000000 -0400 -+++ nfs-utils-1.2.0/support/export/xtab.c 2009-10-20 08:47:26.000000000 -0400 -@@ -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.0/support/include/exportfs.h.save nfs-utils-1.2.0/support/include/exportfs.h ---- nfs-utils-1.2.0/support/include/exportfs.h.save 2009-10-20 08:46:50.000000000 -0400 -+++ nfs-utils-1.2.0/support/include/exportfs.h 2009-10-20 08:47:07.000000000 -0400 -@@ -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.0/support/include/nfs/export.h.save nfs-utils-1.2.0/support/include/nfs/export.h ---- nfs-utils-1.2.0/support/include/nfs/export.h.save 2009-10-20 08:46:50.000000000 -0400 -+++ nfs-utils-1.2.0/support/include/nfs/export.h 2009-10-20 08:47:07.000000000 -0400 -@@ -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.0/support/include/nfslib.h.save nfs-utils-1.2.0/support/include/nfslib.h ---- nfs-utils-1.2.0/support/include/nfslib.h.save 2009-10-20 08:46:50.000000000 -0400 -+++ nfs-utils-1.2.0/support/include/nfslib.h 2009-10-20 08:47:07.000000000 -0400 -@@ -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.0/support/include/v4root.h ---- /dev/null 2009-10-15 16:13:05.251004788 -0400 -+++ nfs-utils-1.2.0/support/include/v4root.h 2009-10-20 08:47:16.000000000 -0400 -@@ -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.0/utils/mountd/auth.c.save nfs-utils-1.2.0/utils/mountd/auth.c ---- nfs-utils-1.2.0/utils/mountd/auth.c.save 2009-10-20 08:46:50.000000000 -0400 -+++ nfs-utils-1.2.0/utils/mountd/auth.c 2009-10-20 08:47:26.000000000 -0400 -@@ -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.0/utils/mountd/cache.c.save nfs-utils-1.2.0/utils/mountd/cache.c ---- nfs-utils-1.2.0/utils/mountd/cache.c.save 2009-10-20 08:46:50.000000000 -0400 -+++ nfs-utils-1.2.0/utils/mountd/cache.c 2009-10-20 08:47:21.000000000 -0400 -@@ -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.0/utils/mountd/Makefile.am.save nfs-utils-1.2.0/utils/mountd/Makefile.am ---- nfs-utils-1.2.0/utils/mountd/Makefile.am.save 2009-10-20 08:46:50.000000000 -0400 -+++ nfs-utils-1.2.0/utils/mountd/Makefile.am 2009-10-20 08:47:16.000000000 -0400 -@@ -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.0/utils/mountd/v4root.c ---- /dev/null 2009-10-15 16:13:05.251004788 -0400 -+++ nfs-utils-1.2.0/utils/mountd/v4root.c 2009-10-20 08:47:33.000000000 -0400 -@@ -0,0 +1,414 @@ -+/* -+ * 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; -+ int i; -+ char *path, *ptr; -+ char *hostname; -+ -+ if (!v4root_needed) -+ return; -+ -+ if (!v4root_support()) -+ return; -+ -+ pseudo_root = &pr_export; -+ -+ 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++; -+ } -+ -+ free(path); -+ } -+ } -+} -+ -+/* -+ * 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); -+ } -+} diff --git a/nfs-utils-1.2.0-v4root-rel8.patch b/nfs-utils-1.2.0-v4root-rel8.patch new file mode 100644 index 0000000..bd73165 --- /dev/null +++ b/nfs-utils-1.2.0-v4root-rel8.patch @@ -0,0 +1,717 @@ +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); ++ } ++} diff --git a/nfs-utils-1.2.1-rc1.patch b/nfs-utils-1.2.1-rc1.patch deleted file mode 100644 index 70305af..0000000 --- a/nfs-utils-1.2.1-rc1.patch +++ /dev/null @@ -1,173 +0,0 @@ -diff --git a/aclocal/ipv6.m4 b/aclocal/ipv6.m4 -index ba9dcc2..2490f3d 100644 ---- a/aclocal/ipv6.m4 -+++ b/aclocal/ipv6.m4 -@@ -11,7 +11,7 @@ AC_DEFUN([AC_IPV6], [ - - dnl TI-RPC required for IPv6 - if test "$enable_tirpc" = no; then -- AC_MSG_ERROR(['--enable-ipv6' requires '--enable-tirpc'.]) -+ AC_MSG_ERROR(['--enable-ipv6' requires TIRPC support.]) - fi - - dnl IPv6-enabled networking functions required for IPv6 -diff --git a/aclocal/libtirpc.m4 b/aclocal/libtirpc.m4 -index af4c7d3..9f0fde0 100644 ---- a/aclocal/libtirpc.m4 -+++ b/aclocal/libtirpc.m4 -@@ -11,17 +11,29 @@ AC_DEFUN([AC_LIBTIRPC], [ - dnl if --enable-tirpc was specifed, the following components - dnl must be present, and we set up HAVE_ macros for them. - -- if test "$enable_tirpc" = yes; then -+ if test "$enable_tirpc" != "no"; then - - dnl look for the library; add to LIBS if found - AC_CHECK_LIB([tirpc], [clnt_tli_create], , -- [AC_MSG_ERROR([libtirpc not found.])]) -+ [if test "$enable_tirpc" = "yes"; then -+ AC_MSG_ERROR([libtirpc not found.]) -+ else -+ AC_MSG_WARN([libtirpc not found. TIRPC disabled!]) -+ enable_tirpc="no" -+ fi]) -+ fi - -+ if test "$enable_tirpc" != "no"; then - dnl also must have the headers installed where we expect - dnl look for headers; add -I compiler option if found -- AC_CHECK_HEADERS([${tirpc_header_dir}/netconfig.h], , -- [AC_MSG_ERROR([libtirpc headers not found.])]) -- AC_SUBST([AM_CPPFLAGS], ["-I${tirpc_header_dir}"]) -+ AC_CHECK_HEADERS([${tirpc_header_dir}/netconfig.h], -+ AC_SUBST([AM_CPPFLAGS], ["-I${tirpc_header_dir}"]), -+ [if test "$enable_tirpc" = "yes"; then -+ AC_MSG_ERROR([libtirpc headers not found.]) -+ else -+ AC_MSG_WARN([libtirpc headers not found. TIRPC disabled!]) -+ enable_tirpc="no" -+ fi]) - - fi - -diff --git a/configure.ac b/configure.ac -index e09e1d9..e0ca70e 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -1,6 +1,6 @@ - dnl Process this file with autoconf to produce a configure script. - dnl --AC_INIT([linux nfs-utils],[1.1.6],[linux-nfs@vger.kernel.org],[nfs-utils]) -+AC_INIT([linux nfs-utils],[1.2.0],[linux-nfs@vger.kernel.org],[nfs-utils]) - AC_CANONICAL_BUILD([]) - AC_CANONICAL_HOST([]) - AC_CONFIG_MACRO_DIR(aclocal) -@@ -120,9 +120,9 @@ AC_ARG_ENABLE(mount, - AM_CONDITIONAL(CONFIG_MOUNT, [test "$enable_mount" = "yes"]) - AC_ARG_ENABLE(tirpc, - [AC_HELP_STRING([--enable-tirpc], -- [enable use of TI-RPC @<:@default=no@:>@])], -+ [enable use of TI-RPC @<:@default=yes@:>@])], - enable_tirpc=$enableval, -- enable_tirpc=no) -+ enable_tirpc='yes') - AC_ARG_ENABLE(ipv6, - [AC_HELP_STRING([--enable-ipv6], - [enable support for IPv6 @<:@default=no@:>@])], -diff --git a/support/nfs/closeall.c b/support/nfs/closeall.c -index cc7fb3b..38fb162 100644 ---- a/support/nfs/closeall.c -+++ b/support/nfs/closeall.c -@@ -7,19 +7,24 @@ - #include - #include - #include -+#include - - void - closeall(int min) - { -+ char *endp; -+ long n; - DIR *dir = opendir("/proc/self/fd"); -+ - if (dir != NULL) { - int dfd = dirfd(dir); - struct dirent *d; - - while ((d = readdir(dir)) != NULL) { -- char *endp; -- long n = strtol(d->d_name, &endp, 10); -- if (*endp != '\0' && n >= min && n != dfd) -+ errno = 0; -+ n = strtol(d->d_name, &endp, 10); -+ if (!errno && *endp == '\0' && endp != d->d_name && -+ n >= min && n != dfd) - (void) close(n); - } - closedir(dir); -diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c -index f6949db..40a2b4d 100644 ---- a/utils/gssd/gssd.c -+++ b/utils/gssd/gssd.c -@@ -171,6 +171,8 @@ main(int argc, char *argv[]) - - initerr(progname, verbosity, fg); - #ifdef HAVE_AUTHGSS_SET_DEBUG_LEVEL -+ if (verbosity && rpc_verbosity == 0) -+ rpc_verbosity = verbosity; - authgss_set_debug_level(rpc_verbosity); - #else - if (rpc_verbosity > 0) -diff --git a/utils/gssd/svcgssd.c b/utils/gssd/svcgssd.c -index 6ca0e8d..69d2a69 100644 ---- a/utils/gssd/svcgssd.c -+++ b/utils/gssd/svcgssd.c -@@ -208,6 +208,8 @@ main(int argc, char *argv[]) - - initerr(progname, verbosity, fg); - #ifdef HAVE_AUTHGSS_SET_DEBUG_LEVEL -+ if (verbosity && rpc_verbosity == 0) -+ rpc_verbosity = verbosity; - authgss_set_debug_level(rpc_verbosity); - #else - if (rpc_verbosity > 0) -@@ -215,6 +217,8 @@ main(int argc, char *argv[]) - "support setting debug level\n"); - #endif - #ifdef HAVE_NFS4_SET_DEBUG -+ if (verbosity && idmap_verbosity == 0) -+ idmap_verbosity = verbosity; - nfs4_set_debug(idmap_verbosity, NULL); - #else - if (idmap_verbosity > 0) -diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c -index 25d292b..b59f939 100644 ---- a/utils/mountd/mountd.c -+++ b/utils/mountd/mountd.c -@@ -467,8 +467,12 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret, - return NULL; - } - } else { -- if (exp->m_exported<1) -+ int did_export = 0; -+ retry: -+ if (exp->m_exported<1) { - export_export(exp); -+ did_export = 1; -+ } - if (!exp->m_xtabent) - xtab_append(exp); - -@@ -482,6 +486,11 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret, - fh = getfh_old ((struct sockaddr *) sin, - stb.st_dev, stb.st_ino); - } -+ if (fh == NULL && !did_export) { -+ exp->m_exported = 0; -+ goto retry; -+ } -+ - if (fh == NULL) { - xlog(L_WARNING, "getfh failed: %s", strerror(errno)); - *error = NFSERR_ACCES; diff --git a/nfs-utils-1.2.1-rc2.patch b/nfs-utils-1.2.1-rc2.patch deleted file mode 100644 index 455b3fc..0000000 --- a/nfs-utils-1.2.1-rc2.patch +++ /dev/null @@ -1,1966 +0,0 @@ -diff --git a/.gitignore b/.gitignore -index cfc329a..632609e 100644 ---- a/.gitignore -+++ b/.gitignore -@@ -13,6 +13,11 @@ ltmain.sh - Makefile.in - missing - support/include/config.h.in -+aclocal/libtool.m4 -+aclocal/ltoptions.m4 -+aclocal/ltsugar.m4 -+aclocal/ltversion.m4 -+aclocal/lt~obsolete.m4 - # files generated by configure - confdefs.h - config.status -diff --git a/support/include/nfsrpc.h b/support/include/nfsrpc.h -index 543c35b..dff6af7 100644 ---- a/support/include/nfsrpc.h -+++ b/support/include/nfsrpc.h -@@ -49,6 +49,25 @@ - #define NSMPROG ((rpcprog_t)100024) - #endif - -+/** -+ * nfs_clear_rpc_createerr - zap all error reporting fields -+ * -+ */ -+static inline void nfs_clear_rpc_createerr(void) -+{ -+ memset(&rpc_createerr, 0, sizeof(rpc_createerr)); -+} -+ -+/* -+ * Extract port value from a socket address -+ */ -+extern uint16_t nfs_get_port(const struct sockaddr *); -+ -+/* -+ * Set port value in a socket address -+ */ -+extern void nfs_set_port(struct sockaddr *, const uint16_t); -+ - /* - * Look up an RPC program name in /etc/rpc - */ -@@ -73,8 +92,7 @@ extern CLIENT *nfs_get_priv_rpcclient( const struct sockaddr *, - /* - * Convert a socket address to a universal address - */ --extern char *nfs_sockaddr2universal(const struct sockaddr *, -- const socklen_t); -+extern char *nfs_sockaddr2universal(const struct sockaddr *); - - /* - * Extract port number from a universal address -@@ -114,7 +132,6 @@ extern unsigned short nfs_rpcb_getaddr(const struct sockaddr *, - const socklen_t, - const unsigned short, - const struct sockaddr *, -- const socklen_t, - const rpcprog_t, - const rpcvers_t, - const unsigned short, -diff --git a/support/nfs/getport.c b/support/nfs/getport.c -index cf1677e..4bdf556 100644 ---- a/support/nfs/getport.c -+++ b/support/nfs/getport.c -@@ -65,77 +65,52 @@ static const rpcvers_t default_rpcb_version = RPCBVERS_4; - static const rpcvers_t default_rpcb_version = PMAPVERS; - #endif /* !HAVE_LIBTIRPC */ - --#ifdef HAVE_DECL_AI_ADDRCONFIG - /* -- * getaddrinfo(3) generates a usable loopback address based on how the -- * local network interfaces are configured. RFC 3484 requires that the -- * results are sorted so that the first result has the best likelihood -- * of working, so we try just that first result. -+ * Historical: Map TCP connect timeouts to timeout -+ * error code used by UDP. -+ */ -+static void -+nfs_gp_map_tcp_errorcodes(const unsigned short protocol) -+{ -+ if (protocol != IPPROTO_TCP) -+ return; -+ -+ switch (rpc_createerr.cf_error.re_errno) { -+ case ETIMEDOUT: -+ rpc_createerr.cf_stat = RPC_TIMEDOUT; -+ break; -+ case ECONNREFUSED: -+ rpc_createerr.cf_stat = RPC_CANTRECV; -+ break; -+ } -+} -+ -+/* -+ * There's no easy way to tell how the local system's networking -+ * and rpcbind is configured (ie. whether we want to use IPv6 or -+ * IPv4 loopback to contact RPC services on the local host). We -+ * punt and simply try to look up "localhost". - * - * Returns TRUE on success. - */ - static int nfs_gp_loopback_address(struct sockaddr *sap, socklen_t *salen) - { - struct addrinfo *gai_results; -- struct addrinfo gai_hint = { -- .ai_flags = AI_ADDRCONFIG, -- }; -- socklen_t len = *salen; - int ret = 0; - -- if (getaddrinfo(NULL, "sunrpc", &gai_hint, &gai_results)) -+ if (getaddrinfo("localhost", NULL, NULL, &gai_results)) - return 0; - -- switch (gai_results->ai_addr->sa_family) { -- case AF_INET: -- case AF_INET6: -- if (len >= gai_results->ai_addrlen) { -- memcpy(sap, gai_results->ai_addr, -- gai_results->ai_addrlen); -- *salen = gai_results->ai_addrlen; -- ret = 1; -- } -+ if (*salen >= gai_results->ai_addrlen) { -+ memcpy(sap, gai_results->ai_addr, -+ gai_results->ai_addrlen); -+ *salen = gai_results->ai_addrlen; -+ ret = 1; - } - - freeaddrinfo(gai_results); - return ret; - } --#else --/* -- * Old versions of getaddrinfo(3) don't support AI_ADDRCONFIG, so we -- * have a fallback for building on legacy systems. -- */ --static int nfs_gp_loopback_address(struct sockaddr *sap, socklen_t *salen) --{ -- struct sockaddr_in *sin = (struct sockaddr_in *)sap; -- -- memset(sin, 0, sizeof(*sin)); -- -- sin->sin_family = AF_INET; -- sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK); -- *salen = sizeof(*sin); -- -- return 1; --} --#endif -- --/* -- * Plant port number in @sap. @port is already in network byte order. -- */ --static void nfs_gp_set_port(struct sockaddr *sap, const in_port_t port) --{ -- struct sockaddr_in *sin = (struct sockaddr_in *)sap; -- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; -- -- switch (sap->sa_family) { -- case AF_INET: -- sin->sin_port = port; -- break; -- case AF_INET6: -- sin6->sin6_port = port; -- break; -- } --} - - /* - * Look up a network service in /etc/services and return the -@@ -201,7 +176,7 @@ static in_port_t nfs_gp_get_rpcb_port(const unsigned short protocol) - * client. Otherwise returns NULL, and rpc_createerr.cf_stat is set to - * reflect the error. - */ --static CLIENT *nfs_gp_get_rpcbclient(const struct sockaddr *sap, -+static CLIENT *nfs_gp_get_rpcbclient(struct sockaddr *sap, - const socklen_t salen, - const unsigned short transport, - const rpcvers_t version, -@@ -214,15 +189,14 @@ static CLIENT *nfs_gp_get_rpcbclient(const struct sockaddr *sap, - "sunrpc", - NULL, - }; -- struct sockaddr_storage address; -- struct sockaddr *saddr = (struct sockaddr *)&address; - rpcprog_t rpcb_prog = nfs_getrpcbyname(RPCBPROG, rpcb_pgmtbl); -+ CLIENT *clnt; - -- memcpy(saddr, sap, (size_t)salen); -- nfs_gp_set_port(saddr, nfs_gp_get_rpcb_port(transport)); -- -- return nfs_get_rpcclient(saddr, salen, transport, rpcb_prog, -- version, timeout); -+ nfs_set_port(sap, ntohs(nfs_gp_get_rpcb_port(transport))); -+ clnt = nfs_get_rpcclient(sap, salen, transport, rpcb_prog, -+ version, timeout); -+ nfs_gp_map_tcp_errorcodes(transport); -+ return clnt; - } - - /* -@@ -352,7 +326,6 @@ int nfs_universal2port(const char *uaddr) - /** - * nfs_sockaddr2universal - convert a sockaddr to a "universal address" - * @sap: pointer to a socket address -- * @salen: length of socket address - * - * Universal addresses (defined in RFC 1833) are used when calling an - * rpcbind daemon via protocol versions 3 or 4.. -@@ -361,81 +334,56 @@ int nfs_universal2port(const char *uaddr) - * the returned string. Otherwise NULL is returned and - * rpc_createerr.cf_stat is set to reflect the error. - * -+ * inet_ntop(3) is used here, since getnameinfo(3) is not available -+ * in some earlier glibc releases, and we don't require support for -+ * scope IDs for universal addresses. - */ --#ifdef HAVE_GETNAMEINFO -- --char *nfs_sockaddr2universal(const struct sockaddr *sap, -- const socklen_t salen) -+char *nfs_sockaddr2universal(const struct sockaddr *sap) - { -- struct sockaddr_un *sun = (struct sockaddr_un *)sap; -- char buf[NI_MAXHOST]; -+ const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sap; -+ const struct sockaddr_un *sun = (const struct sockaddr_un *)sap; -+ const struct sockaddr_in *sin = (const struct sockaddr_in *)sap; -+ char buf[INET6_ADDRSTRLEN + 8 /* for port information */]; - uint16_t port; -+ size_t count; -+ char *result; -+ int len; - - switch (sap->sa_family) { - case AF_LOCAL: - return strndup(sun->sun_path, sizeof(sun->sun_path)); - case AF_INET: -- if (getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf), -- NULL, 0, NI_NUMERICHOST) != 0) -+ if (inet_ntop(AF_INET, (const void *)&sin->sin_addr.s_addr, -+ buf, (socklen_t)sizeof(buf)) == NULL) - goto out_err; -- port = ntohs(((struct sockaddr_in *)sap)->sin_port); -+ port = ntohs(sin->sin_port); - break; - case AF_INET6: -- if (getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf), -- NULL, 0, NI_NUMERICHOST) != 0) -+ if (inet_ntop(AF_INET6, (const void *)&sin6->sin6_addr, -+ buf, (socklen_t)sizeof(buf)) == NULL) - goto out_err; -- port = ntohs(((struct sockaddr_in6 *)sap)->sin6_port); -+ port = ntohs(sin6->sin6_port); - break; - default: - goto out_err; - } - -- (void)snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ".%u.%u", -+ count = sizeof(buf) - strlen(buf); -+ len = snprintf(buf + strlen(buf), count, ".%u.%u", - (unsigned)(port >> 8), (unsigned)(port & 0xff)); -- -- return strdup(buf); -- --out_err: -- rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE; -- return NULL; --} -- --#else /* HAVE_GETNAMEINFO */ -- --char *nfs_sockaddr2universal(const struct sockaddr *sap, -- const socklen_t salen) --{ -- struct sockaddr_un *sun = (struct sockaddr_un *)sap; -- char buf[NI_MAXHOST]; -- uint16_t port; -- char *addr; -- -- switch (sap->sa_family) { -- case AF_LOCAL: -- return strndup(sun->sun_path, sizeof(sun->sun_path)); -- case AF_INET: -- addr = inet_ntoa(((struct sockaddr_in *)sap)->sin_addr); -- if (addr != NULL && strlen(addr) > sizeof(buf)) -- goto out_err; -- strcpy(buf, addr); -- port = ntohs(((struct sockaddr_in *)sap)->sin_port); -- break; -- default: -+ /* before glibc 2.0.6, snprintf(3) could return -1 */ -+ if (len < 0 || (size_t)len > count) - goto out_err; -- } - -- (void)snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ".%u.%u", -- (unsigned)(port >> 8), (unsigned)(port & 0xff)); -- -- return strdup(buf); -+ result = strdup(buf); -+ if (result != NULL) -+ return result; - - out_err: - rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE; - return NULL; - } - --#endif /* HAVE_GETNAMEINFO */ -- - /* - * Send a NULL request to the indicated RPC service. - * -@@ -450,6 +398,10 @@ static int nfs_gp_ping(CLIENT *client, struct timeval timeout) - (xdrproc_t)xdr_void, NULL, - timeout); - -+ if (status != RPC_SUCCESS) { -+ rpc_createerr.cf_stat = status; -+ CLNT_GETERR(client, &rpc_createerr.cf_error); -+ } - return (int)(status == RPC_SUCCESS); - } - -@@ -458,15 +410,10 @@ static int nfs_gp_ping(CLIENT *client, struct timeval timeout) - /* - * Initialize the rpcb argument for a GETADDR request. - * -- * The rpcbind daemon ignores the parms.r_owner field in GETADDR -- * requests, but we plant an eye-catcher to help distinguish these -- * requests in network traces. -- * - * Returns 1 if successful, and caller must free strings pointed - * to by r_netid and r_addr; otherwise 0. - */ - static int nfs_gp_init_rpcb_parms(const struct sockaddr *sap, -- const socklen_t salen, - const rpcprog_t program, - const rpcvers_t version, - const unsigned short protocol, -@@ -478,7 +425,7 @@ static int nfs_gp_init_rpcb_parms(const struct sockaddr *sap, - if (netid == NULL) - return 0; - -- addr = nfs_sockaddr2universal(sap, salen); -+ addr = nfs_sockaddr2universal(sap); - if (addr == NULL) { - free(netid); - return 0; -@@ -489,7 +436,7 @@ static int nfs_gp_init_rpcb_parms(const struct sockaddr *sap, - parms->r_vers = version; - parms->r_netid = netid; - parms->r_addr = addr; -- parms->r_owner = "nfs-utils"; /* eye-catcher */ -+ parms->r_owner = ""; - - return 1; - } -@@ -531,7 +478,7 @@ static unsigned short nfs_gp_rpcb_getaddr(CLIENT *client, - case RPC_SUCCESS: - if ((uaddr == NULL) || (uaddr[0] == '\0')) { - rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; -- continue; -+ return 0; - } - - port = nfs_universal2port(uaddr); -@@ -587,7 +534,7 @@ static unsigned long nfs_gp_pmap_getport(CLIENT *client, - - if (status != RPC_SUCCESS) { - rpc_createerr.cf_stat = status; -- clnt_geterr(client, &rpc_createerr.cf_error); -+ CLNT_GETERR(client, &rpc_createerr.cf_error); - port = 0; - } else if (port == 0) - rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; -@@ -599,7 +546,6 @@ static unsigned long nfs_gp_pmap_getport(CLIENT *client, - - static unsigned short nfs_gp_getport_rpcb(CLIENT *client, - const struct sockaddr *sap, -- const socklen_t salen, - const rpcprog_t program, - const rpcvers_t version, - const unsigned short protocol, -@@ -608,8 +554,8 @@ static unsigned short nfs_gp_getport_rpcb(CLIENT *client, - unsigned short port = 0; - struct rpcb parms; - -- if (nfs_gp_init_rpcb_parms(sap, salen, program, -- version, protocol, &parms) != 0) { -+ if (nfs_gp_init_rpcb_parms(sap, program, version, -+ protocol, &parms) != 0) { - port = nfs_gp_rpcb_getaddr(client, &parms, timeout); - nfs_gp_free_rpcb_parms(&parms); - } -@@ -645,7 +591,6 @@ static unsigned long nfs_gp_getport_pmap(CLIENT *client, - */ - static unsigned short nfs_gp_getport(CLIENT *client, - const struct sockaddr *sap, -- const socklen_t salen, - const rpcprog_t program, - const rpcvers_t version, - const unsigned short protocol, -@@ -654,7 +599,7 @@ static unsigned short nfs_gp_getport(CLIENT *client, - switch (sap->sa_family) { - #ifdef HAVE_LIBTIRPC - case AF_INET6: -- return nfs_gp_getport_rpcb(client, sap, salen, program, -+ return nfs_gp_getport_rpcb(client, sap, program, - version, protocol, timeout); - #endif /* HAVE_LIBTIRPC */ - case AF_INET: -@@ -667,7 +612,7 @@ static unsigned short nfs_gp_getport(CLIENT *client, - } - - /** -- * nfs_rcp_ping - Determine if RPC service is responding to requests -+ * nfs_rpc_ping - Determine if RPC service is responding to requests - * @sap: pointer to address of server to query (port is already filled in) - * @salen: length of server address - * @program: requested RPC program number -@@ -682,6 +627,8 @@ int nfs_rpc_ping(const struct sockaddr *sap, const socklen_t salen, - const rpcprog_t program, const rpcvers_t version, - const unsigned short protocol, const struct timeval *timeout) - { -+ struct sockaddr_storage address; -+ struct sockaddr *saddr = (struct sockaddr *)&address; - CLIENT *client; - struct timeval tout = { -1, 0 }; - int result = 0; -@@ -689,9 +636,14 @@ int nfs_rpc_ping(const struct sockaddr *sap, const socklen_t salen, - if (timeout != NULL) - tout = *timeout; - -- client = nfs_get_rpcclient(sap, salen, protocol, program, version, &tout); -+ nfs_clear_rpc_createerr(); -+ -+ memcpy(saddr, sap, (size_t)salen); -+ client = nfs_get_rpcclient(saddr, salen, protocol, -+ program, version, &tout); - if (client != NULL) { - result = nfs_gp_ping(client, tout); -+ nfs_gp_map_tcp_errorcodes(protocol); - CLNT_DESTROY(client); - } - -@@ -744,14 +696,19 @@ unsigned short nfs_getport(const struct sockaddr *sap, - const rpcvers_t version, - const unsigned short protocol) - { -+ struct sockaddr_storage address; -+ struct sockaddr *saddr = (struct sockaddr *)&address; - struct timeval timeout = { -1, 0 }; - unsigned short port = 0; - CLIENT *client; - -- client = nfs_gp_get_rpcbclient(sap, salen, protocol, -+ nfs_clear_rpc_createerr(); -+ -+ memcpy(saddr, sap, (size_t)salen); -+ client = nfs_gp_get_rpcbclient(saddr, salen, protocol, - default_rpcb_version, &timeout); - if (client != NULL) { -- port = nfs_gp_getport(client, sap, salen, program, -+ port = nfs_gp_getport(client, saddr, program, - version, protocol, timeout); - CLNT_DESTROY(client); - } -@@ -786,10 +743,12 @@ int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen, - CLIENT *client; - int result = 0; - -+ nfs_clear_rpc_createerr(); -+ - client = nfs_gp_get_rpcbclient(sap, salen, protocol, - default_rpcb_version, &timeout); - if (client != NULL) { -- port = nfs_gp_getport(client, sap, salen, program, -+ port = nfs_gp_getport(client, sap, program, - version, protocol, timeout); - CLNT_DESTROY(client); - client = NULL; -@@ -800,18 +759,21 @@ int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen, - struct sockaddr *saddr = (struct sockaddr *)&address; - - memcpy(saddr, sap, (size_t)salen); -- nfs_gp_set_port(saddr, htons(port)); -+ nfs_set_port(saddr, port); -+ -+ nfs_clear_rpc_createerr(); - - client = nfs_get_rpcclient(saddr, salen, protocol, - program, version, &timeout); - if (client != NULL) { - result = nfs_gp_ping(client, timeout); -+ nfs_gp_map_tcp_errorcodes(protocol); - CLNT_DESTROY(client); - } - } - - if (result) -- nfs_gp_set_port(sap, htons(port)); -+ nfs_set_port(sap, port); - - return result; - } -@@ -840,13 +802,6 @@ int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen, - * isn't listening on /var/run/rpcbind.sock), send a query via UDP to localhost - * (UDP doesn't leave a socket in TIME_WAIT, and the timeout is a relatively - * short 3 seconds). -- * -- * getaddrinfo(3) generates a usable loopback address. RFC 3484 requires that -- * the results are sorted so that the first result has the best likelihood of -- * working, so we try just that first result. If IPv6 is all that is -- * available, we are sure to generate an AF_INET6 loopback address and use -- * rpcbindv4/v3 GETADDR. AF_INET6 requests go via rpcbind v4/3 in order to -- * detect if the requested RPC service supports AF_INET6 or not. - */ - unsigned short nfs_getlocalport(const rpcprot_t program, - const rpcvers_t version, -@@ -867,11 +822,13 @@ unsigned short nfs_getlocalport(const rpcprot_t program, - CLIENT *client; - struct timeval timeout = { -1, 0 }; - -+ nfs_clear_rpc_createerr(); -+ - client = nfs_gp_get_rpcbclient(sap, salen, 0, RPCBVERS_4, &timeout); - if (client != NULL) { - struct rpcb parms; - -- if (nfs_gp_init_rpcb_parms(sap, salen, program, version, -+ if (nfs_gp_init_rpcb_parms(sap, program, version, - protocol, &parms) != 0) { - port = nfs_gp_rpcb_getaddr(client, &parms, timeout); - nfs_gp_free_rpcb_parms(&parms); -@@ -881,6 +838,8 @@ unsigned short nfs_getlocalport(const rpcprot_t program, - #endif /* NFS_GP_LOCAL */ - - if (port == 0) { -+ nfs_clear_rpc_createerr(); -+ - if (nfs_gp_loopback_address(lb_addr, &lb_len)) { - port = nfs_getport(lb_addr, lb_len, - program, version, protocol); -@@ -897,7 +856,6 @@ unsigned short nfs_getlocalport(const rpcprot_t program, - * @salen: length of server address - * @transport: transport protocol to use for the query - * @addr: pointer to r_addr address -- * @addrlen: length of address - * @program: requested RPC program number - * @version: requested RPC version number - * @protocol: requested IPPROTO_ value of transport protocol -@@ -928,12 +886,13 @@ unsigned short nfs_rpcb_getaddr(const struct sockaddr *sap, - const socklen_t salen, - const unsigned short transport, - const struct sockaddr *addr, -- const socklen_t addrlen, - const rpcprog_t program, - const rpcvers_t version, - const unsigned short protocol, - const struct timeval *timeout) - { -+ struct sockaddr_storage address; -+ struct sockaddr *saddr = (struct sockaddr *)&address; - CLIENT *client; - struct rpcb parms; - struct timeval tout = { -1, 0 }; -@@ -942,9 +901,13 @@ unsigned short nfs_rpcb_getaddr(const struct sockaddr *sap, - if (timeout != NULL) - tout = *timeout; - -- client = nfs_gp_get_rpcbclient(sap, salen, transport, RPCBVERS_4, &tout); -+ nfs_clear_rpc_createerr(); -+ -+ memcpy(saddr, sap, (size_t)salen); -+ client = nfs_gp_get_rpcbclient(saddr, salen, transport, -+ RPCBVERS_4, &tout); - if (client != NULL) { -- if (nfs_gp_init_rpcb_parms(addr, addrlen, program, version, -+ if (nfs_gp_init_rpcb_parms(addr, program, version, - protocol, &parms) != 0) { - port = nfs_gp_rpcb_getaddr(client, &parms, tout); - nfs_gp_free_rpcb_parms(&parms); -@@ -957,16 +920,17 @@ unsigned short nfs_rpcb_getaddr(const struct sockaddr *sap, - - #else /* !HAVE_LIBTIRPC */ - --unsigned short nfs_rpcb_getaddr(const struct sockaddr *sap, -- const socklen_t salen, -- const unsigned short transport, -- const struct sockaddr *addr, -- const socklen_t addrlen, -- const rpcprog_t program, -- const rpcvers_t version, -- const unsigned short protocol, -- const struct timeval *timeout) -+unsigned short nfs_rpcb_getaddr(__attribute__((unused)) const struct sockaddr *sap, -+ __attribute__((unused)) const socklen_t salen, -+ __attribute__((unused)) const unsigned short transport, -+ __attribute__((unused)) const struct sockaddr *addr, -+ __attribute__((unused)) const rpcprog_t program, -+ __attribute__((unused)) const rpcvers_t version, -+ __attribute__((unused)) const unsigned short protocol, -+ __attribute__((unused)) const struct timeval *timeout) - { -+ nfs_clear_rpc_createerr(); -+ - rpc_createerr.cf_stat = RPC_UNKNOWNADDR; - return 0; - } -@@ -1008,6 +972,8 @@ unsigned long nfs_pmap_getport(const struct sockaddr_in *sin, - const unsigned long protocol, - const struct timeval *timeout) - { -+ struct sockaddr_in address; -+ struct sockaddr *saddr = (struct sockaddr *)&address; - CLIENT *client; - struct pmap parms = { - .pm_prog = program, -@@ -1020,8 +986,10 @@ unsigned long nfs_pmap_getport(const struct sockaddr_in *sin, - if (timeout != NULL) - tout = *timeout; - -- client = nfs_gp_get_rpcbclient((struct sockaddr *)sin, -- (socklen_t)sizeof(*sin), -+ nfs_clear_rpc_createerr(); -+ -+ memcpy(saddr, sin, sizeof(address)); -+ client = nfs_gp_get_rpcbclient(saddr, (socklen_t)sizeof(*sin), - transport, PMAPVERS, &tout); - if (client != NULL) { - port = nfs_gp_pmap_getport(client, &parms, tout); -diff --git a/support/nfs/rpc_socket.c b/support/nfs/rpc_socket.c -index cebf83d..9c20f61 100644 ---- a/support/nfs/rpc_socket.c -+++ b/support/nfs/rpc_socket.c -@@ -132,7 +132,7 @@ static int nfs_bind(const int sock, const sa_family_t family) - return -1; - } - --#ifdef IPV6_SUPPORTED -+#ifdef HAVE_LIBTIRPC - - /* - * Bind a socket using an unused privileged source port. -@@ -162,7 +162,7 @@ static int nfs_bindresvport(const int sock, const sa_family_t family) - return -1; - } - --#else /* !IPV6_SUPPORTED */ -+#else /* !HAVE_LIBTIRPC */ - - /* - * Bind a socket using an unused privileged source port. -@@ -180,7 +180,7 @@ static int nfs_bindresvport(const int sock, const sa_family_t family) - return bindresvport(sock, NULL); - } - --#endif /* !IPV6_SUPPORTED */ -+#endif /* !HAVE_LIBTIRPC */ - - /* - * Perform a non-blocking connect on the socket fd. -@@ -326,7 +326,9 @@ static CLIENT *nfs_get_udpclient(const struct sockaddr *sap, - version, *timeout, &sock); - #endif /* !HAVE_LIBTIRPC */ - if (client != NULL) { -- CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)timeout); -+ struct timeval retry_timeout = { 1, 0 }; -+ CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, -+ (char *)&retry_timeout); - CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL); - } else - (void)close(sock); -@@ -414,6 +416,49 @@ static CLIENT *nfs_get_tcpclient(const struct sockaddr *sap, - } - - /** -+ * nfs_get_port - extract port value from a socket address -+ * @sap: pointer to socket address -+ * -+ * Returns port value in host byte order. -+ */ -+uint16_t -+nfs_get_port(const struct sockaddr *sap) -+{ -+ const struct sockaddr_in *sin = (const struct sockaddr_in *)sap; -+ const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sap; -+ -+ switch (sap->sa_family) { -+ case AF_INET: -+ return ntohs(sin->sin_port); -+ case AF_INET6: -+ return ntohs(sin6->sin6_port); -+ } -+ return 0; -+} -+ -+/** -+ * nfs_set_port - set port value in a socket address -+ * @sap: pointer to socket address -+ * @port: port value to set -+ * -+ */ -+void -+nfs_set_port(struct sockaddr *sap, const uint16_t port) -+{ -+ struct sockaddr_in *sin = (struct sockaddr_in *)sap; -+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; -+ -+ switch (sap->sa_family) { -+ case AF_INET: -+ sin->sin_port = htons(port); -+ break; -+ case AF_INET6: -+ sin6->sin6_port = htons(port); -+ break; -+ } -+} -+ -+/** - * nfs_get_rpcclient - acquire an RPC client - * @sap: pointer to socket address of RPC server - * @salen: length of socket address -@@ -438,27 +483,21 @@ CLIENT *nfs_get_rpcclient(const struct sockaddr *sap, - const rpcvers_t version, - struct timeval *timeout) - { -- struct sockaddr_in *sin = (struct sockaddr_in *)sap; -- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; -+ nfs_clear_rpc_createerr(); - - switch (sap->sa_family) { - case AF_LOCAL: - return nfs_get_localclient(sap, salen, program, - version, timeout); - case AF_INET: -- if (sin->sin_port == 0) { -- rpc_createerr.cf_stat = RPC_UNKNOWNADDR; -- return NULL; -- } -- break; - case AF_INET6: -- if (sin6->sin6_port == 0) { -+ if (nfs_get_port(sap) == 0) { - rpc_createerr.cf_stat = RPC_UNKNOWNADDR; - return NULL; - } - break; - default: -- rpc_createerr.cf_stat = RPC_UNKNOWNHOST; -+ rpc_createerr.cf_stat = RPC_UNKNOWNADDR; - return NULL; - } - -@@ -501,27 +540,21 @@ CLIENT *nfs_get_priv_rpcclient(const struct sockaddr *sap, - const rpcvers_t version, - struct timeval *timeout) - { -- struct sockaddr_in *sin = (struct sockaddr_in *)sap; -- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; -+ nfs_clear_rpc_createerr(); - - switch (sap->sa_family) { - case AF_LOCAL: - return nfs_get_localclient(sap, salen, program, - version, timeout); - case AF_INET: -- if (sin->sin_port == 0) { -- rpc_createerr.cf_stat = RPC_UNKNOWNADDR; -- return NULL; -- } -- break; - case AF_INET6: -- if (sin6->sin6_port == 0) { -+ if (nfs_get_port(sap) == 0) { - rpc_createerr.cf_stat = RPC_UNKNOWNADDR; - return NULL; - } - break; - default: -- rpc_createerr.cf_stat = RPC_UNKNOWNHOST; -+ rpc_createerr.cf_stat = RPC_UNKNOWNADDR; - return NULL; - } - -diff --git a/utils/gssd/svcgssd.c b/utils/gssd/svcgssd.c -index 69d2a69..729b6a6 100644 ---- a/utils/gssd/svcgssd.c -+++ b/utils/gssd/svcgssd.c -@@ -117,10 +117,16 @@ mydaemon(int nochdir, int noclose) - - if (noclose == 0) { - tempfd = open("/dev/null", O_RDWR); -- dup2(tempfd, 0); -- dup2(tempfd, 1); -- dup2(tempfd, 2); -- closeall(3); -+ if (tempfd >= 0) { -+ dup2(tempfd, 0); -+ dup2(tempfd, 1); -+ dup2(tempfd, 2); -+ close(tempfd); -+ } else { -+ printerr(1, "mydaemon: can't open /dev/null: errno %d " -+ "(%s)\n", errno, strerror(errno)); -+ exit(1); -+ } - } - - return; -diff --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c -index b690e21..9cbe96c 100644 ---- a/utils/idmapd/idmapd.c -+++ b/utils/idmapd/idmapd.c -@@ -978,9 +978,12 @@ mydaemon(int nochdir, int noclose) - dup2(tempfd, 0); - dup2(tempfd, 1); - dup2(tempfd, 2); -- closeall(3); -- } else -- closeall(0); -+ close(tempfd); -+ } else { -+ err(1, "mydaemon: can't open /dev/null: errno %d", -+ errno); -+ exit(1); -+ } - } - - return; -diff --git a/utils/mount/error.c b/utils/mount/error.c -index 5c9d3f2..1b64bd7 100644 ---- a/utils/mount/error.c -+++ b/utils/mount/error.c -@@ -70,9 +70,15 @@ static int rpc_strerror(int spos) - pos = snprintf(tmp, (erreob - tmp), - _("System Error: %s"), - strerror(cf_errno)); -- else -- pos = snprintf(tmp, (erreob - tmp), -- _("RPC Error:%s"), estr); -+ else { -+ if (cf_errno) -+ pos = snprintf(tmp, (erreob - tmp), -+ _("RPC Error:%s; errno = %s"), -+ estr, strerror(cf_errno)); -+ else -+ pos = snprintf(tmp, (erreob - tmp), -+ _("RPC Error:%s"), estr); -+ } - } - return pos; - } -@@ -300,6 +306,8 @@ void umount_error(int err, const char *dev) - #define EDQUOT ENOSPC - #endif - -+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) -+ - static struct { - enum nfsstat stat; - int errnum; -@@ -329,19 +337,17 @@ static struct { - #endif - /* Throw in some NFSv3 values for even more fun (HP returns these) */ - { 71, EREMOTE }, -- -- { -1, EIO } - }; - --char *nfs_strerror(int stat) -+char *nfs_strerror(unsigned int stat) - { -- int i; -+ unsigned int i; - static char buf[256]; - -- for (i = 0; nfs_errtbl[i].stat != -1; i++) { -+ for (i = 0; i < ARRAY_SIZE(nfs_errtbl); i++) { - if (nfs_errtbl[i].stat == stat) - return strerror(nfs_errtbl[i].errnum); - } -- sprintf(buf, _("unknown nfs status return value: %d"), stat); -+ sprintf(buf, _("unknown nfs status return value: %u"), stat); - return buf; - } -diff --git a/utils/mount/error.h b/utils/mount/error.h -index 7126de5..42b28cf 100644 ---- a/utils/mount/error.h -+++ b/utils/mount/error.h -@@ -24,7 +24,7 @@ - #ifndef _NFS_UTILS_MOUNT_ERROR_H - #define _NFS_UTILS_MOUNT_ERROR_H - --char *nfs_strerror(int); -+char *nfs_strerror(unsigned int); - - void mount_error(const char *, const char *, int); - void rpc_mount_errors(char *, int, int); -diff --git a/utils/mount/fstab.c b/utils/mount/fstab.c -index 7668167..2775d0b 100644 ---- a/utils/mount/fstab.c -+++ b/utils/mount/fstab.c -@@ -285,7 +285,7 @@ handler (int sig) { - } - - static void --setlkw_timeout (int sig) { -+setlkw_timeout (__attribute__((unused)) int sig) { - /* nothing, fcntl will fail anyway */ - } - -diff --git a/utils/mount/mount.c b/utils/mount/mount.c -index 06e2804..a668cd9 100644 ---- a/utils/mount/mount.c -+++ b/utils/mount/mount.c -@@ -156,7 +156,7 @@ static void parse_opts(const char *options, int *flags, char **extra_opts); - */ - static void discover_nfs_mount_data_version(void) - { -- int kernel_version = linux_version_code(); -+ unsigned int kernel_version = linux_version_code(); - - if (kernel_version) { - if (kernel_version < MAKE_VERSION(2, 1, 32)) -@@ -417,7 +417,7 @@ static int chk_mountpoint(char *mount_point) - - static int try_mount(char *spec, char *mount_point, int flags, - char *fs_type, char **extra_opts, char *mount_opts, -- int fake, int nomtab, int bg) -+ int fake, int bg) - { - int ret; - -@@ -582,7 +582,7 @@ int main(int argc, char *argv[]) - } - - mnt_err = try_mount(spec, mount_point, flags, fs_type, &extra_opts, -- mount_opts, fake, nomtab, FOREGROUND); -+ mount_opts, fake, FOREGROUND); - if (mnt_err == EX_BG) { - printf(_("%s: backgrounding \"%s\"\n"), - progname, spec); -@@ -600,7 +600,7 @@ int main(int argc, char *argv[]) - - mnt_err = try_mount(spec, mount_point, flags, fs_type, - &extra_opts, mount_opts, fake, -- nomtab, BACKGROUND); -+ BACKGROUND); - if (verbose && mnt_err) - printf(_("%s: giving up \"%s\"\n"), - progname, spec); -diff --git a/utils/mount/network.c b/utils/mount/network.c -index 04a62ab..f6fa5fd 100644 ---- a/utils/mount/network.c -+++ b/utils/mount/network.c -@@ -170,21 +170,6 @@ static const unsigned long probe_mnt3_first[] = { - 0, - }; - --static void nfs_set_port(struct sockaddr *sap, const unsigned short port) --{ -- switch (sap->sa_family) { -- case AF_INET: -- ((struct sockaddr_in *)sap)->sin_port = htons(port); -- break; -- case AF_INET6: -- ((struct sockaddr_in6 *)sap)->sin6_port = htons(port); -- break; -- default: -- nfs_error(_("%s: unrecognized address family in %s"), -- progname, __func__); -- } --} -- - static int nfs_lookup(const char *hostname, const sa_family_t family, - struct sockaddr *sap, socklen_t *salen) - { -@@ -270,7 +255,6 @@ int nfs_gethostbyname(const char *hostname, struct sockaddr_in *sin) - /** - * nfs_string_to_sockaddr - convert string address to sockaddr - * @address: pointer to presentation format address to convert -- * @addrlen: length of presentation address - * @sap: pointer to socket address buffer to fill in - * @salen: IN: length of address buffer - * OUT: length of converted socket address -@@ -284,8 +268,8 @@ int nfs_gethostbyname(const char *hostname, struct sockaddr_in *sin) - * See RFC 4038 section 5.1 or RFC 3513 section 2.2 for more details - * on presenting IPv6 addresses as text strings. - */ --int nfs_string_to_sockaddr(const char *address, const size_t addrlen, -- struct sockaddr *sap, socklen_t *salen) -+int nfs_string_to_sockaddr(const char *address, struct sockaddr *sap, -+ socklen_t *salen) - { - struct addrinfo *gai_results; - struct addrinfo gai_hint = { -@@ -509,6 +493,21 @@ static void nfs_pp_debug(const struct sockaddr *sap, const socklen_t salen, - port); - } - -+static void nfs_pp_debug2(const char *str) -+{ -+ if (!verbose) -+ return; -+ -+ if (rpc_createerr.cf_error.re_status == RPC_CANTRECV || -+ rpc_createerr.cf_error.re_status == RPC_CANTSEND) -+ nfs_error(_("%s: portmap query %s%s - %s"), -+ progname, str, clnt_spcreateerror(""), -+ strerror(rpc_createerr.cf_error.re_errno)); -+ else -+ nfs_error(_("%s: portmap query %s%s"), -+ progname, str, clnt_spcreateerror("")); -+} -+ - /* - * Use the portmapper to discover whether or not the service we want is - * available. The lists 'versions' and 'protos' define ordered sequences -@@ -538,9 +537,11 @@ static int nfs_probe_port(const struct sockaddr *sap, const socklen_t salen, - memcpy(saddr, sap, salen); - p_prot = prot ? &prot : protos; - p_vers = vers ? &vers : versions; -- rpc_createerr.cf_stat = 0; - - for (;;) { -+ if (verbose) -+ printf(_("%s: prog %lu, trying vers=%lu, prot=%u\n"), -+ progname, prog, *p_vers, *p_prot); - p_port = nfs_getport(saddr, salen, prog, *p_vers, *p_prot); - if (p_port) { - if (!port || port == p_port) { -@@ -550,28 +551,31 @@ static int nfs_probe_port(const struct sockaddr *sap, const socklen_t salen, - if (nfs_rpc_ping(saddr, salen, prog, - *p_vers, *p_prot, NULL)) - goto out_ok; -- } -+ } else -+ rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; - } - if (rpc_createerr.cf_stat != RPC_PROGNOTREGISTERED && - rpc_createerr.cf_stat != RPC_TIMEDOUT && - rpc_createerr.cf_stat != RPC_CANTRECV && - rpc_createerr.cf_stat != RPC_PROGVERSMISMATCH) -- goto out_bad; -+ break; - - if (!prot) { -- if (*++p_prot) -+ if (*++p_prot) { -+ nfs_pp_debug2("retrying"); - continue; -+ } - p_prot = protos; - } - if (rpc_createerr.cf_stat == RPC_TIMEDOUT || - rpc_createerr.cf_stat == RPC_CANTRECV) -- goto out_bad; -+ break; - - if (vers || !*++p_vers) - break; - } - --out_bad: -+ nfs_pp_debug2("failed"); - return 0; - - out_ok: -@@ -581,7 +585,7 @@ out_ok: - pmap->pm_prot = *p_prot; - if (!port) - pmap->pm_port = p_port; -- rpc_createerr.cf_stat = 0; -+ nfs_clear_rpc_createerr(); - return 1; - } - -@@ -778,8 +782,8 @@ int start_statd(void) - execl(START_STATD, START_STATD, NULL); - exit(1); - case -1: /* error */ -- nfs_error(_("fork failed: %s"), -- strerror(errno)); -+ nfs_error(_("%s: fork failed: %s"), -+ progname, strerror(errno)); - break; - default: /* parent */ - waitpid(pid, NULL,0); -@@ -844,10 +848,14 @@ int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen, - (xdrproc_t)xdr_dirpath, (caddr_t)argp, - (xdrproc_t)xdr_void, NULL, - timeout); -- if (verbose && res != RPC_SUCCESS) -- nfs_error(_("%s: UMNT call failed: %s"), -- progname, clnt_sperrno(res)); -+ if (res != RPC_SUCCESS) { -+ rpc_createerr.cf_stat = res; -+ CLNT_GETERR(client, &rpc_createerr.cf_error); -+ if (verbose) -+ nfs_error(_("%s: UMNT call failed: %s"), -+ progname, clnt_sperrno(res)); - -+ } - auth_destroy(client->cl_auth); - CLNT_DESTROY(client); - -@@ -1098,7 +1106,7 @@ static int nfs_ca_sockname(const struct sockaddr *sap, const socklen_t salen, - * - * Returns 1 and fills in @buf if successful; otherwise, zero. - */ --static int nfs_ca_gai(const struct sockaddr *sap, const socklen_t salen, -+static int nfs_ca_gai(const struct sockaddr *sap, - struct sockaddr *buf, socklen_t *buflen) - { - struct addrinfo *gai_results; -@@ -1139,7 +1147,7 @@ int nfs_callback_address(const struct sockaddr *sap, const socklen_t salen, - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)buf; - - if (nfs_ca_sockname(sap, salen, buf, buflen) == 0) -- if (nfs_ca_gai(sap, salen, buf, buflen) == 0) -+ if (nfs_ca_gai(sap, buf, buflen) == 0) - goto out_failed; - - /* -@@ -1154,173 +1162,285 @@ int nfs_callback_address(const struct sockaddr *sap, const socklen_t salen, - out_failed: - *buflen = 0; - if (verbose) -- nfs_error(_("%s: failed to construct callback address")); -+ nfs_error(_("%s: failed to construct callback address"), -+ progname); - return 0; -- - } - - /* -- * "nfsprog" is only supported by the legacy mount command. The -+ * "nfsprog" is supported only by the legacy mount command. The - * kernel mount client does not support this option. - * -- * Returns the value set by the nfsprog= option, the value of -- * the RPC NFS program specified in /etc/rpc, or a baked-in -- * default program number, if all fails. -+ * Returns TRUE if @program contains a valid value for this option, -+ * or FALSE if the option was specified with an invalid value. - */ --static rpcprog_t nfs_nfs_program(struct mount_options *options) -+static int -+nfs_nfs_program(struct mount_options *options, unsigned long *program) - { - long tmp; - -- if (po_get_numeric(options, "nfsprog", &tmp) == PO_FOUND) -- if (tmp >= 0) -- return tmp; -- return nfs_getrpcbyname(NFSPROG, nfs_nfs_pgmtbl); --} -+ switch (po_get_numeric(options, "nfsprog", &tmp)) { -+ case PO_NOT_FOUND: -+ break; -+ case PO_FOUND: -+ if (tmp > 0) { -+ *program = tmp; -+ return 1; -+ } -+ case PO_BAD_VALUE: -+ return 0; -+ } - -+ /* -+ * NFS RPC program wasn't specified. The RPC program -+ * cannot be determined via an rpcbind query. -+ */ -+ *program = nfs_getrpcbyname(NFSPROG, nfs_nfs_pgmtbl); -+ return 1; -+} - - /* -- * Returns the RPC version number specified by the given mount -- * options for the NFS service, or zero if all fails. -+ * Returns TRUE if @version contains a valid value for this option, -+ * or FALSE if the option was specified with an invalid value. - */ --static rpcvers_t nfs_nfs_version(struct mount_options *options) -+static int -+nfs_nfs_version(struct mount_options *options, unsigned long *version) - { - long tmp; - - switch (po_rightmost(options, nfs_version_opttbl)) { - case 0: /* v2 */ -- return 2; -+ *version = 2; -+ return 1; - case 1: /* v3 */ -- return 3; -+ *version = 3; -+ return 1; - case 2: /* vers */ -- if (po_get_numeric(options, "vers", &tmp) == PO_FOUND) -- if (tmp >= 2 && tmp <= 3) -- return tmp; -- break; -+ switch (po_get_numeric(options, "vers", &tmp)) { -+ case PO_FOUND: -+ if (tmp >= 2 && tmp <= 3) { -+ *version = tmp; -+ return 1; -+ } -+ return 0; -+ case PO_NOT_FOUND: -+ nfs_error(_("%s: option parsing error\n"), -+ progname); -+ case PO_BAD_VALUE: -+ return 0; -+ } - case 3: /* nfsvers */ -- if (po_get_numeric(options, "nfsvers", &tmp) == PO_FOUND) -- if (tmp >= 2 && tmp <= 3) -- return tmp; -- break; -+ switch (po_get_numeric(options, "nfsvers", &tmp)) { -+ case PO_FOUND: -+ if (tmp >= 2 && tmp <= 3) { -+ *version = tmp; -+ return 1; -+ } -+ return 0; -+ case PO_NOT_FOUND: -+ nfs_error(_("%s: option parsing error\n"), -+ progname); -+ case PO_BAD_VALUE: -+ return 0; -+ } - } - -- return 0; -+ /* -+ * NFS version wasn't specified. The pmap version value -+ * will be filled in later by an rpcbind query in this case. -+ */ -+ *version = 0; -+ return 1; - } - - /* -- * Returns the NFS transport protocol specified by the given mount options -- * -- * Returns the IPPROTO_ value specified by the given mount options, or -- * IPPROTO_UDP if all fails. -+ * Returns TRUE if @protocol contains a valid value for this option, -+ * or FALSE if the option was specified with an invalid value. - */ --static unsigned short nfs_nfs_protocol(struct mount_options *options) -+static int -+nfs_nfs_protocol(struct mount_options *options, unsigned long *protocol) - { - char *option; - - switch (po_rightmost(options, nfs_transport_opttbl)) { -+ case 0: /* udp */ -+ *protocol = IPPROTO_UDP; -+ return 1; - case 1: /* tcp */ -- return IPPROTO_TCP; -+ *protocol = IPPROTO_TCP; -+ return 1; - case 2: /* proto */ - option = po_get(options, "proto"); - if (option) { -- if (strcmp(option, "tcp") == 0) -- return IPPROTO_TCP; -- if (strcmp(option, "udp") == 0) -- return IPPROTO_UDP; -+ if (strcmp(option, "tcp") == 0) { -+ *protocol = IPPROTO_TCP; -+ return 1; -+ } -+ if (strcmp(option, "udp") == 0) { -+ *protocol = IPPROTO_UDP; -+ return 1; -+ } -+ return 0; - } - } - -- return IPPROTO_UDP; -+ /* -+ * NFS transport protocol wasn't specified. The pmap -+ * protocol value will be filled in later by an rpcbind -+ * query in this case. -+ */ -+ *protocol = 0; -+ return 1; - } - - /* -- * Returns the NFS server's port number specified by the given -- * mount options, or zero if all fails. Zero results in a portmap -- * query to discover the server's mountd service port. -- * -- * port=0 will guarantee an rpcbind request precedes the first -- * NFS RPC so the client can determine the server's port number. -+ * Returns TRUE if @port contains a valid value for this option, -+ * or FALSE if the option was specified with an invalid value. - */ --static unsigned short nfs_nfs_port(struct mount_options *options) -+static int -+nfs_nfs_port(struct mount_options *options, unsigned long *port) - { - long tmp; - -- if (po_get_numeric(options, "port", &tmp) == PO_FOUND) -- if (tmp >= 0 && tmp <= 65535) -- return tmp; -- return 0; -+ switch (po_get_numeric(options, "port", &tmp)) { -+ case PO_NOT_FOUND: -+ break; -+ case PO_FOUND: -+ if (tmp >= 1 && tmp <= 65535) { -+ *port = tmp; -+ return 1; -+ } -+ case PO_BAD_VALUE: -+ return 0; -+ } -+ -+ /* -+ * NFS service port wasn't specified. The pmap port value -+ * will be filled in later by an rpcbind query in this case. -+ */ -+ *port = 0; -+ return 1; - } - - /* -- * "mountprog" is only supported by the legacy mount command. The -+ * "mountprog" is supported only by the legacy mount command. The - * kernel mount client does not support this option. - * -- * Returns the value set by the mountprog= option, the value of -- * the RPC mount program specified in /etc/rpc, or a baked-in -- * default program number, if all fails. -+ * Returns TRUE if @program contains a valid value for this option, -+ * or FALSE if the option was specified with an invalid value. - */ --static rpcprog_t nfs_mount_program(struct mount_options *options) -+static int -+nfs_mount_program(struct mount_options *options, unsigned long *program) - { - long tmp; - -- if (po_get_numeric(options, "mountprog", &tmp) == PO_FOUND) -- if (tmp >= 0) -- return tmp; -- return nfs_getrpcbyname(MOUNTPROG, nfs_mnt_pgmtbl); -+ switch (po_get_numeric(options, "mountprog", &tmp)) { -+ case PO_NOT_FOUND: -+ break; -+ case PO_FOUND: -+ if (tmp > 0) { -+ *program = tmp; -+ return 1; -+ } -+ case PO_BAD_VALUE: -+ return 0; -+ } -+ -+ /* -+ * MNT RPC program wasn't specified. The RPC program -+ * cannot be determined via an rpcbind query. -+ */ -+ *program = nfs_getrpcbyname(MOUNTPROG, nfs_mnt_pgmtbl); -+ return 1; - } - - /* -- * Returns the RPC version number specified by the given mount options, -- * or the version "3" if all fails. -+ * Returns TRUE if @version contains a valid value for this option, -+ * or FALSE if the option was specified with an invalid value. - */ --static rpcvers_t nfs_mount_version(struct mount_options *options) -+static int -+nfs_mount_version(struct mount_options *options, unsigned long *version) - { - long tmp; - -- if (po_get_numeric(options, "mountvers", &tmp) == PO_FOUND) -- if (tmp >= 1 && tmp <= 4) -- return tmp; -+ switch (po_get_numeric(options, "mountvers", &tmp)) { -+ case PO_NOT_FOUND: -+ break; -+ case PO_FOUND: -+ if (tmp >= 1 && tmp <= 4) { -+ *version = tmp; -+ return 1; -+ } -+ case PO_BAD_VALUE: -+ return 0; -+ } - -- return nfsvers_to_mnt(nfs_nfs_version(options)); -+ /* -+ * MNT version wasn't specified. The pmap version value -+ * will be filled in later by an rpcbind query in this case. -+ */ -+ *version = 0; -+ return 1; - } - - /* -- * Returns the transport protocol to use for the mount service -- * -- * Returns the IPPROTO_ value specified by the mountproto option, or -- * if that doesn't exist, the IPPROTO_ value specified for NFS -- * itself. -+ * Returns TRUE if @protocol contains a valid value for this option, -+ * or FALSE if the option was specified with an invalid value. - */ --static unsigned short nfs_mount_protocol(struct mount_options *options) -+static int -+nfs_mount_protocol(struct mount_options *options, unsigned long *protocol) - { - char *option; - - option = po_get(options, "mountproto"); - if (option) { -- if (strcmp(option, "tcp") == 0) -- return IPPROTO_TCP; -- if (strcmp(option, "udp") == 0) -- return IPPROTO_UDP; -+ if (strcmp(option, "tcp") == 0) { -+ *protocol = IPPROTO_TCP; -+ return 1; -+ } -+ if (strcmp(option, "udp") == 0) { -+ *protocol = IPPROTO_UDP; -+ return 1; -+ } -+ return 0; - } - -- return nfs_nfs_protocol(options); -+ /* -+ * MNT transport protocol wasn't specified. If the NFS -+ * transport protocol was specified, use that; otherwise -+ * set @protocol to zero. The pmap protocol value will -+ * be filled in later by an rpcbind query in this case. -+ */ -+ return nfs_nfs_protocol(options, protocol); - } - - /* -- * Returns the mountd server's port number specified by the given -- * mount options, or zero if all fails. Zero results in a portmap -- * query to discover the server's mountd service port. -- * -- * port=0 will guarantee an rpcbind request precedes the mount -- * RPC so the client can determine the server's port number. -+ * Returns TRUE if @port contains a valid value for this option, -+ * or FALSE if the option was specified with an invalid value. - */ --static unsigned short nfs_mount_port(struct mount_options *options) -+static int -+nfs_mount_port(struct mount_options *options, unsigned long *port) - { - long tmp; - -- if (po_get_numeric(options, "mountport", &tmp) == PO_FOUND) -- if (tmp >= 0 && tmp <= 65535) -- return tmp; -- return 0; -+ switch (po_get_numeric(options, "mountport", &tmp)) { -+ case PO_NOT_FOUND: -+ break; -+ case PO_FOUND: -+ if (tmp >= 1 && tmp <= 65535) { -+ *port = tmp; -+ return 1; -+ } -+ case PO_BAD_VALUE: -+ return 0; -+ } -+ -+ /* -+ * MNT service port wasn't specified. The pmap port value -+ * will be filled in later by an rpcbind query in this case. -+ */ -+ *port = 0; -+ return 1; - } - - /** -@@ -1329,17 +1449,29 @@ static unsigned short nfs_mount_port(struct mount_options *options) - * @nfs_pmap: OUT: pointer to pmap arguments for NFS server - * @mnt_pmap: OUT: pointer to pmap arguments for mountd server - * -+ * Returns TRUE if the pmap options specified in @options have valid -+ * values; otherwise FALSE is returned. - */ --void nfs_options2pmap(struct mount_options *options, -- struct pmap *nfs_pmap, struct pmap *mnt_pmap) -+int nfs_options2pmap(struct mount_options *options, -+ struct pmap *nfs_pmap, struct pmap *mnt_pmap) - { -- nfs_pmap->pm_prog = nfs_nfs_program(options); -- nfs_pmap->pm_vers = nfs_nfs_version(options); -- nfs_pmap->pm_prot = nfs_nfs_protocol(options); -- nfs_pmap->pm_port = nfs_nfs_port(options); -- -- mnt_pmap->pm_prog = nfs_mount_program(options); -- mnt_pmap->pm_vers = nfs_mount_version(options); -- mnt_pmap->pm_prot = nfs_mount_protocol(options); -- mnt_pmap->pm_port = nfs_mount_port(options); -+ if (!nfs_nfs_program(options, &nfs_pmap->pm_prog)) -+ return 0; -+ if (!nfs_nfs_version(options, &nfs_pmap->pm_vers)) -+ return 0; -+ if (!nfs_nfs_protocol(options, &nfs_pmap->pm_prot)) -+ return 0; -+ if (!nfs_nfs_port(options, &nfs_pmap->pm_port)) -+ return 0; -+ -+ if (!nfs_mount_program(options, &mnt_pmap->pm_prog)) -+ return 0; -+ if (!nfs_mount_version(options, &mnt_pmap->pm_vers)) -+ return 0; -+ if (!nfs_mount_protocol(options, &mnt_pmap->pm_prot)) -+ return 0; -+ if (!nfs_mount_port(options, &mnt_pmap->pm_port)) -+ return 0; -+ -+ return 1; - } -diff --git a/utils/mount/network.h b/utils/mount/network.h -index b3f9bd2..db5134c 100644 ---- a/utils/mount/network.h -+++ b/utils/mount/network.h -@@ -45,8 +45,7 @@ int nfs_probe_bothports(const struct sockaddr *, const socklen_t, - const socklen_t, struct pmap *); - int nfs_gethostbyname(const char *, struct sockaddr_in *); - int nfs_name_to_address(const char *, struct sockaddr *, socklen_t *); --int nfs_string_to_sockaddr(const char *, const size_t, -- struct sockaddr *, socklen_t *); -+int nfs_string_to_sockaddr(const char *, struct sockaddr *, socklen_t *); - int nfs_present_sockaddr(const struct sockaddr *, - const socklen_t, char *, const size_t); - int nfs_callback_address(const struct sockaddr *, const socklen_t, -@@ -57,7 +56,7 @@ int clnt_ping(struct sockaddr_in *, const unsigned long, - - struct mount_options; - --void nfs_options2pmap(struct mount_options *, -+int nfs_options2pmap(struct mount_options *, - struct pmap *, struct pmap *); - - int start_statd(void); -diff --git a/utils/mount/nfsumount.c b/utils/mount/nfsumount.c -index 9b48cc9..f81db14 100644 ---- a/utils/mount/nfsumount.c -+++ b/utils/mount/nfsumount.c -@@ -174,7 +174,10 @@ static int nfs_umount_do_umnt(struct mount_options *options, - socklen_t salen = sizeof(address); - struct pmap nfs_pmap, mnt_pmap; - -- nfs_options2pmap(options, &nfs_pmap, &mnt_pmap); -+ if (!nfs_options2pmap(options, &nfs_pmap, &mnt_pmap)) { -+ nfs_error(_("%s: bad mount options"), progname); -+ return EX_FAIL; -+ } - - *hostname = nfs_umount_hostname(options, *hostname); - if (!*hostname) { -@@ -333,7 +336,7 @@ int nfsumount(int argc, char *argv[]) - char *opt = hasmntopt(&mc->m, "user"); - struct passwd *pw; - char *comma; -- int len; -+ size_t len; - if (!opt) - goto only_root; - if (opt[4] != '=') -diff --git a/utils/mount/parse_dev.c b/utils/mount/parse_dev.c -index c0a8e18..c8a58b1 100644 ---- a/utils/mount/parse_dev.c -+++ b/utils/mount/parse_dev.c -@@ -183,8 +183,9 @@ static int nfs_parse_square_bracket(const char *dev, - * with the mount request and failing with a cryptic error message - * later. - */ --static int nfs_parse_nfs_url(const char *dev, -- char **hostname, char **pathname) -+static int nfs_parse_nfs_url(__attribute__((unused)) const char *dev, -+ __attribute__((unused)) char **hostname, -+ __attribute__((unused)) char **pathname) - { - nfs_error(_("%s: NFS URLs are not supported"), progname); - return 0; -diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c -index ec95b78..a12ace7 100644 ---- a/utils/mount/stropts.c -+++ b/utils/mount/stropts.c -@@ -130,12 +130,14 @@ static int nfs_append_generic_address_option(const struct sockaddr *sap, - { - char address[NI_MAXHOST]; - char new_option[512]; -+ int len; - - if (!nfs_present_sockaddr(sap, salen, address, sizeof(address))) - goto out_err; - -- if (snprintf(new_option, sizeof(new_option), "%s=%s", -- keyword, address) >= sizeof(new_option)) -+ len = snprintf(new_option, sizeof(new_option), "%s=%s", -+ keyword, address); -+ if (len < 0 || (size_t)len >= sizeof(new_option)) - goto out_err; - - if (po_append(options, new_option) != PO_SUCCEEDED) -@@ -283,27 +285,16 @@ static int nfs_validate_options(struct nfsmount_info *mi) - if (!nfs_append_sloppy_option(mi->options)) - return 0; - -- return nfs_append_addr_option(sap, salen, mi->options); --} -+ if (!nfs_append_addr_option(sap, salen, mi->options)) -+ return 0; - --/* -- * Distinguish between permanent and temporary errors. -- * -- * Returns 0 if the passed-in error is temporary, thus the -- * mount system call should be retried; returns one if the -- * passed-in error is permanent, thus the mount system call -- * should not be retried. -- */ --static int nfs_is_permanent_error(int error) --{ -- switch (error) { -- case ESTALE: -- case ETIMEDOUT: -- case ECONNREFUSED: -- return 0; /* temporary */ -- default: -- return 1; /* permanent */ -- } -+ /* -+ * Update option string to be recorded in /etc/mnttab -+ */ -+ if (po_join(mi->options, mi->extra_opts) == PO_FAILED) -+ return 0; -+ -+ return 1; - } - - /* -@@ -323,16 +314,14 @@ static int nfs_extract_server_addresses(struct mount_options *options, - option = po_get(options, "addr"); - if (option == NULL) - return 0; -- if (!nfs_string_to_sockaddr(option, strlen(option), -- nfs_saddr, nfs_salen)) -+ if (!nfs_string_to_sockaddr(option, nfs_saddr, nfs_salen)) - return 0; - - option = po_get(options, "mountaddr"); - if (option == NULL) { - memcpy(mnt_saddr, nfs_saddr, *nfs_salen); - *mnt_salen = *nfs_salen; -- } else if (!nfs_string_to_sockaddr(option, strlen(option), -- mnt_saddr, mnt_salen)) -+ } else if (!nfs_string_to_sockaddr(option, mnt_saddr, mnt_salen)) - return 0; - - return 1; -@@ -420,206 +409,135 @@ static int nfs_construct_new_options(struct mount_options *options, - * - * To handle version and transport protocol fallback properly, we - * need to parse some of the mount options in order to set up a -- * portmap probe. Mount options that nfs_rewrite_mount_options() -+ * portmap probe. Mount options that nfs_rewrite_pmap_mount_options() - * doesn't recognize are left alone. - * -- * Returns a new group of mount options if successful; otherwise -- * NULL is returned if some failure occurred. -+ * Returns TRUE if rewriting was successful; otherwise -+ * FALSE is returned if some failure occurred. - */ --static struct mount_options *nfs_rewrite_mount_options(char *str) -+static int -+nfs_rewrite_pmap_mount_options(struct mount_options *options) - { -- struct mount_options *options; - struct sockaddr_storage nfs_address; - struct sockaddr *nfs_saddr = (struct sockaddr *)&nfs_address; -- socklen_t nfs_salen; -+ socklen_t nfs_salen = sizeof(nfs_address); - struct pmap nfs_pmap; - struct sockaddr_storage mnt_address; - struct sockaddr *mnt_saddr = (struct sockaddr *)&mnt_address; -- socklen_t mnt_salen; -+ socklen_t mnt_salen = sizeof(mnt_address); - struct pmap mnt_pmap; -+ char *option; - -- options = po_split(str); -- if (!options) { -- errno = EFAULT; -- return NULL; -- } -+ /* -+ * Skip option negotiation for proto=rdma mounts. -+ */ -+ option = po_get(options, "proto"); -+ if (option && strcmp(option, "rdma") == 0) -+ goto out; - -+ /* -+ * Extract just the options needed to contact server. -+ * Bail now if any of these have bad values. -+ */ - if (!nfs_extract_server_addresses(options, nfs_saddr, &nfs_salen, - mnt_saddr, &mnt_salen)) { - errno = EINVAL; -- goto err; -+ return 0; -+ } -+ if (!nfs_options2pmap(options, &nfs_pmap, &mnt_pmap)) { -+ errno = EINVAL; -+ return 0; - } - -- nfs_options2pmap(options, &nfs_pmap, &mnt_pmap); -- -- /* The kernel NFS client doesn't support changing the RPC program -- * number for these services, so reset these fields before probing -- * the server's ports. */ -+ /* -+ * The kernel NFS client doesn't support changing the RPC -+ * program number for these services, so force the value of -+ * these fields before probing the server's ports. -+ */ - nfs_pmap.pm_prog = NFS_PROGRAM; - mnt_pmap.pm_prog = MOUNTPROG; - -+ /* -+ * If the server's rpcbind service isn't available, we can't -+ * negotiate. Bail now if we can't contact it. -+ */ - if (!nfs_probe_bothports(mnt_saddr, mnt_salen, &mnt_pmap, - nfs_saddr, nfs_salen, &nfs_pmap)) { - errno = ESPIPE; -- goto err; -+ return 0; - } - - if (!nfs_construct_new_options(options, &nfs_pmap, &mnt_pmap)) { - errno = EINVAL; -- goto err; -+ return 0; - } - -+out: - errno = 0; -- return options; -- --err: -- po_destroy(options); -- return NULL; -+ return 1; - } - - /* - * Do the mount(2) system call. - * -- * Returns 1 if successful, otherwise zero. -+ * Returns TRUE if successful, otherwise FALSE. - * "errno" is set to reflect the individual error. - */ --static int nfs_sys_mount(const struct nfsmount_info *mi, const char *type, -- const char *options) -+static int nfs_try_mount(struct nfsmount_info *mi) - { -+ char *options = NULL; - int result; - -- result = mount(mi->spec, mi->node, type, -- mi->flags & ~(MS_USER|MS_USERS), options); -- if (verbose && result) { -- int save = errno; -- nfs_error(_("%s: mount(2): %s"), progname, strerror(save)); -- errno = save; -- } -- return !result; --} -- --/* -- * Retry an NFS mount that failed because the requested service isn't -- * available on the server. -- * -- * Returns 1 if successful. Otherwise, returns zero. -- * "errno" is set to reflect the individual error. -- * -- * Side effect: If the retry is successful, both 'options' and -- * 'extra_opts' are updated to reflect the mount options that worked. -- * If the retry fails, 'options' and 'extra_opts' are left unchanged. -- */ --static int nfs_retry_nfs23mount(struct nfsmount_info *mi) --{ -- struct mount_options *retry_options; -- char *retry_str = NULL; -- char **extra_opts = mi->extra_opts; -- -- retry_options = nfs_rewrite_mount_options(*extra_opts); -- if (!retry_options) -- return 0; -- -- if (po_join(retry_options, &retry_str) == PO_FAILED) { -- po_destroy(retry_options); -- errno = EIO; -- return 0; -- } -- -- if (verbose) -- printf(_("%s: text-based options (retry): '%s'\n"), -- progname, retry_str); -- -- if (!nfs_sys_mount(mi, "nfs", retry_str)) { -- po_destroy(retry_options); -- free(retry_str); -- return 0; -+ if (strncmp(mi->type, "nfs4", 4) != 0) { -+ if (!nfs_rewrite_pmap_mount_options(mi->options)) -+ return 0; - } - -- free(*extra_opts); -- *extra_opts = retry_str; -- po_replace(mi->options, retry_options); -- return 1; --} -- --/* -- * Attempt an NFSv2/3 mount via a mount(2) system call. If the kernel -- * claims the requested service isn't supported on the server, probe -- * the server to see what's supported, rewrite the mount options, -- * and retry the request. -- * -- * Returns 1 if successful. Otherwise, returns zero. -- * "errno" is set to reflect the individual error. -- * -- * Side effect: If the retry is successful, both 'options' and -- * 'extra_opts' are updated to reflect the mount options that worked. -- * If the retry fails, 'options' and 'extra_opts' are left unchanged. -- */ --static int nfs_try_nfs23mount(struct nfsmount_info *mi) --{ -- char **extra_opts = mi->extra_opts; -- -- if (po_join(mi->options, extra_opts) == PO_FAILED) { -+ if (po_join(mi->options, &options) == PO_FAILED) { - errno = EIO; - return 0; - } - - if (verbose) -- printf(_("%s: text-based options: '%s'\n"), -- progname, *extra_opts); -+ printf(_("%s: trying text-based options '%s'\n"), -+ progname, options); - - if (mi->fake) - return 1; - -- if (nfs_sys_mount(mi, "nfs", *extra_opts)) -- return 1; -- -- /* -- * The kernel returns EOPNOTSUPP if the RPC bind failed, -- * and EPROTONOSUPPORT if the version isn't supported. -- */ -- if (errno != EOPNOTSUPP && errno != EPROTONOSUPPORT) -- return 0; -- -- return nfs_retry_nfs23mount(mi); --} -- --/* -- * Attempt an NFS v4 mount via a mount(2) system call. -- * -- * Returns 1 if successful. Otherwise, returns zero. -- * "errno" is set to reflect the individual error. -- */ --static int nfs_try_nfs4mount(struct nfsmount_info *mi) --{ -- char **extra_opts = mi->extra_opts; -- -- if (po_join(mi->options, extra_opts) == PO_FAILED) { -- errno = EIO; -- return 0; -+ result = mount(mi->spec, mi->node, mi->type, -+ mi->flags & ~(MS_USER|MS_USERS), options); -+ if (verbose && result) { -+ int save = errno; -+ nfs_error(_("%s: mount(2): %s"), progname, strerror(save)); -+ errno = save; - } -- -- if (verbose) -- printf(_("%s: text-based options: '%s'\n"), -- progname, *extra_opts); -- -- if (mi->fake) -- return 1; -- -- return nfs_sys_mount(mi, "nfs4", *extra_opts); -+ return !result; - } - - /* -- * Perform either an NFSv2/3 mount, or an NFSv4 mount system call. -+ * Distinguish between permanent and temporary errors. - * -- * Returns 1 if successful. Otherwise, returns zero. -- * "errno" is set to reflect the individual error. -+ * Basically, we retry if communication with the server has -+ * failed so far, but fail immediately if there is a local -+ * error (like a bad mount option). -+ * -+ * ESTALE is also a temporary error because some servers -+ * return ESTALE when a share is temporarily offline. -+ * -+ * Returns 1 if we should fail immediately, or 0 if we -+ * should retry. - */ --static int nfs_try_mount(struct nfsmount_info *mi) -+static int nfs_is_permanent_error(int error) - { -- if (strncmp(mi->type, "nfs4", 4) == 0) -- return nfs_try_nfs4mount(mi); -- else -- return nfs_try_nfs23mount(mi); -+ switch (error) { -+ case ESTALE: -+ case ETIMEDOUT: -+ case ECONNREFUSED: -+ return 0; /* temporary */ -+ default: -+ return 1; /* permanent */ -+ } - } - - /* diff --git a/nfs-utils-1.2.1-rc3.patch b/nfs-utils-1.2.1-rc3.patch deleted file mode 100644 index 3322887..0000000 --- a/nfs-utils-1.2.1-rc3.patch +++ /dev/null @@ -1,1003 +0,0 @@ -diff --git a/support/include/nfs/nfs.h b/support/include/nfs/nfs.h -index 00b0028..a64eb0a 100644 ---- a/support/include/nfs/nfs.h -+++ b/support/include/nfs/nfs.h -@@ -42,14 +42,21 @@ struct nfs_fh_old { - #define NFSCTL_GETFD 7 /* get an fh by path (used by mountd) */ - #define NFSCTL_GETFS 8 /* get an fh by path with max size (used by mountd) */ - -+#define NFSCTL_UDPBIT (1 << (17 - 1)) -+#define NFSCTL_TCPBIT (1 << (18 - 1)) -+ - #define NFSCTL_VERUNSET(_cltbits, _v) ((_cltbits) &= ~(1 << ((_v) - 1))) --#define NFSCTL_UDPUNSET(_cltbits) ((_cltbits) &= ~(1 << (17 - 1))) --#define NFSCTL_TCPUNSET(_cltbits) ((_cltbits) &= ~(1 << (18 - 1))) -+#define NFSCTL_UDPUNSET(_cltbits) ((_cltbits) &= ~NFSCTL_UDPBIT) -+#define NFSCTL_TCPUNSET(_cltbits) ((_cltbits) &= ~NFSCTL_TCPBIT) - - #define NFSCTL_VERISSET(_cltbits, _v) ((_cltbits) & (1 << ((_v) - 1))) --#define NFSCTL_UDPISSET(_cltbits) ((_cltbits) & (1 << (17 - 1))) --#define NFSCTL_TCPISSET(_cltbits) ((_cltbits) & (1 << (18 - 1))) -+#define NFSCTL_UDPISSET(_cltbits) ((_cltbits) & NFSCTL_UDPBIT) -+#define NFSCTL_TCPISSET(_cltbits) ((_cltbits) & NFSCTL_TCPBIT) -+ -+#define NFSCTL_UDPSET(_cltbits) ((_cltbits) |= NFSCTL_UDPBIT) -+#define NFSCTL_TCPSET(_cltbits) ((_cltbits) |= NFSCTL_TCPBIT) - -+#define NFSCTL_ANYPROTO(_cltbits) ((_cltbits) & (NFSCTL_UDPBIT | NFSCTL_TCPBIT)) - #define NFSCTL_ALLBITS (~0) - - /* SVC */ -diff --git a/support/include/nfslib.h b/support/include/nfslib.h -index ae98650..537a31e 100644 ---- a/support/include/nfslib.h -+++ b/support/include/nfslib.h -@@ -130,7 +130,6 @@ int wildmat(char *text, char *pattern); - * nfsd library functions. - */ - int nfsctl(int, struct nfsctl_arg *, union nfsctl_res *); --int nfssvc(int port, int nrservs, unsigned int versbits, int minorvers4, unsigned int portbits, char *haddr); - int nfsaddclient(struct nfsctl_client *clp); - int nfsdelclient(struct nfsctl_client *clp); - int nfsexport(struct nfsctl_export *exp); -diff --git a/support/nfs/Makefile.am b/support/nfs/Makefile.am -index 86f52a1..096f56d 100644 ---- a/support/nfs/Makefile.am -+++ b/support/nfs/Makefile.am -@@ -2,7 +2,7 @@ - - noinst_LIBRARIES = libnfs.a - libnfs_a_SOURCES = exports.c rmtab.c xio.c rpcmisc.c rpcdispatch.c \ -- xlog.c xcommon.c wildmat.c nfssvc.c nfsclient.c \ -+ xlog.c xcommon.c wildmat.c nfsclient.c \ - nfsexport.c getfh.c nfsctl.c rpc_socket.c getport.c \ - svc_socket.c cacheio.c closeall.c nfs_mntent.c - -diff --git a/support/nfs/nfssvc.c b/support/nfs/nfssvc.c -deleted file mode 100644 -index 33c15a7..0000000 ---- a/support/nfs/nfssvc.c -+++ /dev/null -@@ -1,187 +0,0 @@ --/* -- * support/nfs/nfssvc.c -- * -- * Run an NFS daemon. -- * -- * Copyright (C) 1995, 1996 Olaf Kirch -- */ -- --#ifdef HAVE_CONFIG_H --#include --#endif -- --#include --#include --#include --#include --#include --#include --#include -- -- --#include "nfslib.h" -- --#define NFSD_PORTS_FILE "/proc/fs/nfsd/portlist" --#define NFSD_VERS_FILE "/proc/fs/nfsd/versions" --#define NFSD_THREAD_FILE "/proc/fs/nfsd/threads" -- --static void --nfssvc_setfds(int port, unsigned int ctlbits, char *haddr) --{ -- int fd, n, on=1; -- char buf[BUFSIZ]; -- int udpfd = -1, tcpfd = -1; -- struct sockaddr_in sin; -- -- fd = open(NFSD_PORTS_FILE, O_RDONLY); -- if (fd < 0) -- return; -- n = read(fd, buf, BUFSIZ); -- close(fd); -- if (n != 0) -- return; -- /* there are no ports currently open, so it is safe to -- * try to open some and pass them through. -- * Note: If the user explicitly asked for 'udp', then -- * we should probably check if that is open, and should -- * open it if not. However we don't yet. All sockets -- * have to be opened when the first daemon is started. -- */ -- fd = open(NFSD_PORTS_FILE, O_WRONLY); -- if (fd < 0) -- return; -- sin.sin_family = AF_INET; -- sin.sin_port = htons(port); -- sin.sin_addr.s_addr = inet_addr(haddr); -- -- if (NFSCTL_UDPISSET(ctlbits)) { -- udpfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); -- if (udpfd < 0) { -- syslog(LOG_ERR, "nfssvc: unable to create UPD socket: " -- "errno %d (%s)\n", errno, strerror(errno)); -- exit(1); -- } -- if (bind(udpfd, (struct sockaddr *)&sin, sizeof(sin)) < 0){ -- syslog(LOG_ERR, "nfssvc: unable to bind UPD socket: " -- "errno %d (%s)\n", errno, strerror(errno)); -- exit(1); -- } -- } -- -- if (NFSCTL_TCPISSET(ctlbits)) { -- tcpfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); -- if (tcpfd < 0) { -- syslog(LOG_ERR, "nfssvc: unable to createt tcp socket: " -- "errno %d (%s)\n", errno, strerror(errno)); -- exit(1); -- } -- if (setsockopt(tcpfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { -- syslog(LOG_ERR, "nfssvc: unable to set SO_REUSEADDR: " -- "errno %d (%s)\n", errno, strerror(errno)); -- exit(1); -- } -- if (bind(tcpfd, (struct sockaddr *)&sin, sizeof(sin)) < 0){ -- syslog(LOG_ERR, "nfssvc: unable to bind TCP socket: " -- "errno %d (%s)\n", errno, strerror(errno)); -- exit(1); -- } -- if (listen(tcpfd, 64) < 0){ -- syslog(LOG_ERR, "nfssvc: unable to create listening socket: " -- "errno %d (%s)\n", errno, strerror(errno)); -- exit(1); -- } -- } -- if (udpfd >= 0) { -- snprintf(buf, BUFSIZ,"%d\n", udpfd); -- if (write(fd, buf, strlen(buf)) != strlen(buf)) { -- syslog(LOG_ERR, -- "nfssvc: writing fds to kernel failed: errno %d (%s)", -- errno, strerror(errno)); -- } -- close(fd); -- fd = -1; -- } -- if (tcpfd >= 0) { -- if (fd < 0) -- fd = open(NFSD_PORTS_FILE, O_WRONLY); -- snprintf(buf, BUFSIZ,"%d\n", tcpfd); -- if (write(fd, buf, strlen(buf)) != strlen(buf)) { -- syslog(LOG_ERR, -- "nfssvc: writing fds to kernel failed: errno %d (%s)", -- errno, strerror(errno)); -- } -- } -- close(fd); -- -- return; --} --static void --nfssvc_versbits(unsigned int ctlbits, int minorvers4) --{ -- int fd, n, off; -- char buf[BUFSIZ], *ptr; -- -- ptr = buf; -- off = 0; -- fd = open(NFSD_VERS_FILE, O_WRONLY); -- if (fd < 0) -- return; -- -- for (n = NFSD_MINVERS; n <= NFSD_MAXVERS; n++) { -- if (NFSCTL_VERISSET(ctlbits, n)) -- off += snprintf(ptr+off, BUFSIZ - off, "+%d ", n); -- else -- off += snprintf(ptr+off, BUFSIZ - off, "-%d ", n); -- } -- n = minorvers4 >= 0 ? minorvers4 : -minorvers4; -- if (n >= NFSD_MINMINORVERS4 && n <= NFSD_MAXMINORVERS4) -- off += snprintf(ptr+off, BUFSIZ - off, "%c4.%d", -- minorvers4 > 0 ? '+' : '-', -- n); -- snprintf(ptr+off, BUFSIZ - off, "\n"); -- if (write(fd, buf, strlen(buf)) != strlen(buf)) { -- syslog(LOG_ERR, "nfssvc: Setting version failed: errno %d (%s)", -- errno, strerror(errno)); -- } -- close(fd); -- -- return; --} --int --nfssvc(int port, int nrservs, unsigned int versbits, int minorvers4, -- unsigned protobits, char *haddr) --{ -- struct nfsctl_arg arg; -- int fd; -- -- /* Note: must set versions before fds so that -- * the ports get registered with portmap against correct -- * versions -- */ -- nfssvc_versbits(versbits, minorvers4); -- nfssvc_setfds(port, protobits, haddr); -- -- fd = open(NFSD_THREAD_FILE, O_WRONLY); -- if (fd < 0) -- fd = open("/proc/fs/nfs/threads", O_WRONLY); -- if (fd >= 0) { -- /* 2.5+ kernel with nfsd filesystem mounted. -- * Just write the number in. -- * Cannot handle port number yet, but does anyone care? -- */ -- char buf[20]; -- int n; -- snprintf(buf, 20,"%d\n", nrservs); -- n = write(fd, buf, strlen(buf)); -- close(fd); -- if (n != strlen(buf)) -- return -1; -- else -- return 0; -- } -- -- arg.ca_version = NFSCTL_VERSION; -- arg.ca_svc.svc_nthreads = nrservs; -- arg.ca_svc.svc_port = port; -- return nfsctl(NFSCTL_SVC, &arg, NULL); --} -diff --git a/utils/nfsd/Makefile.am b/utils/nfsd/Makefile.am -index 445e3fd..c4c6fb0 100644 ---- a/utils/nfsd/Makefile.am -+++ b/utils/nfsd/Makefile.am -@@ -7,10 +7,8 @@ RPCPREFIX = rpc. - KPREFIX = @kprefix@ - sbin_PROGRAMS = nfsd - --nfsd_SOURCES = nfsd.c --nfsd_LDADD = ../../support/export/libexport.a \ -- ../../support/nfs/libnfs.a \ -- ../../support/misc/libmisc.a -+nfsd_SOURCES = nfsd.c nfssvc.c -+nfsd_LDADD = ../../support/nfs/libnfs.a - - MAINTAINERCLEANFILES = Makefile.in - -diff --git a/utils/nfsd/nfsd.c b/utils/nfsd/nfsd.c -index e3c0094..650c593 100644 ---- a/utils/nfsd/nfsd.c -+++ b/utils/nfsd/nfsd.c -@@ -18,13 +18,14 @@ - #include - #include - #include --#include - #include - #include - #include - #include - - #include "nfslib.h" -+#include "nfssvc.h" -+#include "xlog.h" - - static void usage(const char *); - -@@ -37,47 +38,116 @@ static struct option longopts[] = - { "no-udp", 0, 0, 'U' }, - { "port", 1, 0, 'P' }, - { "port", 1, 0, 'p' }, -+ { "debug", 0, 0, 'd' }, -+ { "syslog", 0, 0, 's' }, - { NULL, 0, 0, 0 } - }; --unsigned int protobits = NFSCTL_ALLBITS; --unsigned int versbits = NFSCTL_ALLBITS; --int minorvers4 = NFSD_MAXMINORVERS4; /* nfsv4 minor version */ --char *haddr = NULL; -+ -+/* given a family and ctlbits, disable any that aren't listed in netconfig */ -+#ifdef HAVE_LIBTIRPC -+static void -+nfsd_enable_protos(unsigned int *proto4, unsigned int *proto6) -+{ -+ struct netconfig *nconf; -+ unsigned int *famproto; -+ void *handle; -+ -+ xlog(D_GENERAL, "Checking netconfig for visible protocols."); -+ -+ handle = setnetconfig(); -+ while((nconf = getnetconfig(handle))) { -+ if (!(nconf->nc_flag & NC_VISIBLE)) -+ continue; -+ -+ if (!strcmp(nconf->nc_protofmly, NC_INET)) -+ famproto = proto4; -+ else if (!strcmp(nconf->nc_protofmly, NC_INET6)) -+ famproto = proto6; -+ else -+ continue; -+ -+ if (!strcmp(nconf->nc_proto, NC_TCP)) -+ NFSCTL_TCPSET(*famproto); -+ else if (!strcmp(nconf->nc_proto, NC_UDP)) -+ NFSCTL_UDPSET(*famproto); -+ -+ xlog(D_GENERAL, "Enabling %s %s.", nconf->nc_protofmly, -+ nconf->nc_proto); -+ } -+ endnetconfig(handle); -+ return; -+} -+#else /* HAVE_LIBTIRPC */ -+static void -+nfsd_enable_protos(unsigned int *proto4, unsigned int *proto6) -+{ -+ /* Enable all IPv4 protocols if no TIRPC support */ -+ *proto4 = NFSCTL_ALLBITS; -+ *proto6 = 0; -+} -+#endif /* HAVE_LIBTIRPC */ - - int - main(int argc, char **argv) - { -- int count = 1, c, error, port, fd, found_one; -- struct servent *ent; -- struct hostent *hp; -- char *p; -- -- ent = getservbyname ("nfs", "udp"); -- if (ent != NULL) -- port = ntohs (ent->s_port); -- else -- port = 2049; -- -- while ((c = getopt_long(argc, argv, "H:hN:p:P:TU", longopts, NULL)) != EOF) { -+ int count = 1, c, error = 0, portnum = 0, fd, found_one; -+ char *p, *progname, *port; -+ char *haddr = NULL; -+ int socket_up = 0; -+ int minorvers4 = NFSD_MAXMINORVERS4; /* nfsv4 minor version */ -+ unsigned int versbits = NFSCTL_ALLBITS; -+ unsigned int protobits = NFSCTL_ALLBITS; -+ unsigned int proto4 = 0; -+ unsigned int proto6 = 0; -+ -+ progname = strdup(basename(argv[0])); -+ if (!progname) { -+ fprintf(stderr, "%s: unable to allocate memory.\n", argv[0]); -+ exit(1); -+ } -+ -+ port = strdup("nfs"); -+ if (!port) { -+ fprintf(stderr, "%s: unable to allocate memory.\n", progname); -+ exit(1); -+ } -+ -+ xlog_syslog(0); -+ xlog_stderr(1); -+ -+ while ((c = getopt_long(argc, argv, "dH:hN:p:P:sTU", longopts, NULL)) != EOF) { - switch(c) { -+ case 'd': -+ xlog_config(D_ALL, 1); -+ break; - case 'H': -- if (inet_addr(optarg) != INADDR_NONE) { -- haddr = strdup(optarg); -- } else if ((hp = gethostbyname(optarg)) != NULL) { -- haddr = inet_ntoa((*(struct in_addr*)(hp->h_addr_list[0]))); -- } else { -- fprintf(stderr, "%s: Unknown hostname: %s\n", -- argv[0], optarg); -- usage(argv [0]); -+ /* -+ * for now, this only handles one -H option. Use the -+ * last one specified. -+ */ -+ free(haddr); -+ haddr = strdup(optarg); -+ if (!haddr) { -+ fprintf(stderr, "%s: unable to allocate " -+ "memory.\n", progname); -+ exit(1); - } - break; - case 'P': /* XXX for nfs-server compatibility */ - case 'p': -- port = atoi(optarg); -- if (port <= 0 || port > 65535) { -+ /* only the last -p option has any effect */ -+ portnum = atoi(optarg); -+ if (portnum <= 0 || portnum > 65535) { - fprintf(stderr, "%s: bad port number: %s\n", -- argv[0], optarg); -- usage(argv [0]); -+ progname, optarg); -+ usage(progname); -+ } -+ free(port); -+ port = strdup(optarg); -+ if (!port) { -+ fprintf(stderr, "%s: unable to allocate " -+ "memory.\n", progname); -+ exit(1); - } - break; - case 'N': -@@ -96,80 +166,133 @@ main(int argc, char **argv) - exit(1); - } - break; -+ case 's': -+ xlog_syslog(1); -+ xlog_stderr(0); -+ break; - case 'T': -- NFSCTL_TCPUNSET(protobits); -- break; -+ NFSCTL_TCPUNSET(protobits); -+ break; - case 'U': -- NFSCTL_UDPUNSET(protobits); -- break; -+ NFSCTL_UDPUNSET(protobits); -+ break; - default: - fprintf(stderr, "Invalid argument: '%c'\n", c); - case 'h': -- usage(argv[0]); -+ usage(progname); - } - } -- /* -- * Do some sanity checking, if the ctlbits are set -- */ -- if (!NFSCTL_UDPISSET(protobits) && !NFSCTL_TCPISSET(protobits)) { -- fprintf(stderr, "invalid protocol specified\n"); -- exit(1); -+ -+ if (optind < argc) { -+ if ((count = atoi(argv[optind])) < 0) { -+ /* insane # of servers */ -+ fprintf(stderr, -+ "%s: invalid server count (%d), using 1\n", -+ argv[0], count); -+ count = 1; -+ } else if (count == 0) { -+ /* -+ * don't bother setting anything else if the threads -+ * are coming down anyway. -+ */ -+ socket_up = 1; -+ goto set_threads; -+ } - } -+ -+ xlog_open(progname); -+ -+ nfsd_enable_protos(&proto4, &proto6); -+ -+ if (!NFSCTL_TCPISSET(protobits)) { -+ NFSCTL_TCPUNSET(proto4); -+ NFSCTL_TCPUNSET(proto6); -+ } -+ -+ if (!NFSCTL_UDPISSET(protobits)) { -+ NFSCTL_UDPUNSET(proto4); -+ NFSCTL_UDPUNSET(proto6); -+ } -+ -+ /* make sure that at least one version is enabled */ - found_one = 0; - for (c = NFSD_MINVERS; c <= NFSD_MAXVERS; c++) { - if (NFSCTL_VERISSET(versbits, c)) - found_one = 1; - } - if (!found_one) { -- fprintf(stderr, "no version specified\n"); -+ xlog(L_ERROR, "no version specified"); - exit(1); - } - -- if (NFSCTL_VERISSET(versbits, 4) && !NFSCTL_TCPISSET(protobits)) { -- fprintf(stderr, "version 4 requires the TCP protocol\n"); -+ if (NFSCTL_VERISSET(versbits, 4) && -+ !NFSCTL_TCPISSET(proto4) && -+ !NFSCTL_TCPISSET(proto6)) { -+ xlog(L_ERROR, "version 4 requires the TCP protocol"); - exit(1); - } -- if (haddr == NULL) { -- struct in_addr in = {INADDR_ANY}; -- haddr = strdup(inet_ntoa(in)); -- } - - if (chdir(NFS_STATEDIR)) { -- fprintf(stderr, "%s: chdir(%s) failed: %s\n", -- argv [0], NFS_STATEDIR, strerror(errno)); -+ xlog(L_ERROR, "chdir(%s) failed: %m", NFS_STATEDIR); - exit(1); - } - -- if (optind < argc) { -- if ((count = atoi(argv[optind])) < 0) { -- /* insane # of servers */ -- fprintf(stderr, -- "%s: invalid server count (%d), using 1\n", -- argv[0], count); -- count = 1; -- } -+ /* can only change number of threads if nfsd is already up */ -+ if (nfssvc_inuse()) { -+ socket_up = 1; -+ goto set_threads; - } -- /* KLUDGE ALERT: -- Some kernels let nfsd kernel threads inherit open files -- from the program that spawns them (i.e. us). So close -- everything before spawning kernel threads. --Chip */ -+ -+ /* -+ * must set versions before the fd's so that the right versions get -+ * registered with rpcbind. Note that on older kernels w/o the right -+ * interfaces, these are a no-op. -+ */ -+ nfssvc_setvers(versbits, minorvers4); -+ -+ error = nfssvc_set_sockets(AF_INET, proto4, haddr, port); -+ if (!error) -+ socket_up = 1; -+ -+#ifdef IPV6_SUPPORTED -+ error = nfssvc_set_sockets(AF_INET6, proto6, haddr, port); -+ if (!error) -+ socket_up = 1; -+#endif /* IPV6_SUPPORTED */ -+ -+set_threads: -+ /* don't start any threads if unable to hand off any sockets */ -+ if (!socket_up) { -+ xlog(L_ERROR, "unable to set any sockets for nfsd"); -+ goto out; -+ } -+ error = 0; -+ -+ /* -+ * KLUDGE ALERT: -+ * Some kernels let nfsd kernel threads inherit open files -+ * from the program that spawns them (i.e. us). So close -+ * everything before spawning kernel threads. --Chip -+ */ - fd = open("/dev/null", O_RDWR); - if (fd == -1) -- perror("/dev/null"); -+ xlog(L_ERROR, "Unable to open /dev/null: %m"); - else { -+ /* switch xlog output to syslog since stderr is being closed */ -+ xlog_syslog(1); -+ xlog_stderr(0); - (void) dup2(fd, 0); - (void) dup2(fd, 1); - (void) dup2(fd, 2); - } - closeall(3); - -- openlog("nfsd", LOG_PID, LOG_DAEMON); -- if ((error = nfssvc(port, count, versbits, minorvers4, protobits, haddr)) < 0) { -- int e = errno; -- syslog(LOG_ERR, "nfssvc: %s", strerror(e)); -- closelog(); -- } -- -+ if ((error = nfssvc_threads(portnum, count)) < 0) -+ xlog(L_ERROR, "error starting threads: errno %d (%m)", errno); -+out: -+ free(port); -+ free(haddr); -+ free(progname); - return (error != 0); - } - -@@ -177,7 +300,7 @@ static void - usage(const char *prog) - { - fprintf(stderr, "Usage:\n" -- "%s [-H hostname] [-p|-P|--port port] [-N|--no-nfs-version version ] [-T|--no-tcp] [-U|--no-udp] nrservs\n", -+ "%s [-d|--debug] [-H hostname] [-p|-P|--port port] [-N|--no-nfs-version version ] [-s|--syslog] [-T|--no-tcp] [-U|--no-udp] nrservs\n", - prog); - exit(2); - } -diff --git a/utils/nfsd/nfsd.man b/utils/nfsd/nfsd.man -index 4eb3e21..d8988d2 100644 ---- a/utils/nfsd/nfsd.man -+++ b/utils/nfsd/nfsd.man -@@ -13,8 +13,9 @@ The - program implements the user level part of the NFS service. The - main functionality is handled by the - .B nfsd --kernel module; the user space program merely starts the specified --number of kernel threads. -+kernel module. The user space program merely specifies what sort of sockets -+the kernel service should listen on, what NFS versions it should support, and -+how many kernel threads it should use. - .P - The - .B rpc.mountd -@@ -22,6 +23,9 @@ server provides an ancillary service needed to satisfy mount requests - by NFS clients. - .SH OPTIONS - .TP -+.B \-d " or " \-\-debug -+enable logging of debugging messages -+.TP - .B \-H " or " \-\-host hostname - specify a particular hostname (or address) that NFS requests will - be accepted on. By default, -@@ -45,6 +49,14 @@ does not offer certain versions of NFS. The current version of - .B rpc.nfsd - can support both NFS version 2,3 and the newer version 4. - .TP -+.B \-s " or " \-\-syslog -+By default, -+.B rpc.nfsd -+logs error messages (and debug messages, if enabled) to stderr. This option makes -+.B rpc.nfsd -+log these messages to syslog instead. Note that errors encountered during -+option processing will still be logged to stderr regardless of this option. -+.TP - .B \-T " or " \-\-no-tcp - Disable - .B rpc.nfsd -@@ -75,12 +87,19 @@ In particular - .B rpc.nfsd 0 - will stop all threads and thus close any open connections. - -+.SH NOTES -+If the program is built with TI-RPC support, it will enable any protocol and -+address family combinations that are marked visible in the -+.B netconfig -+database. -+ - .SH SEE ALSO - .BR rpc.mountd (8), - .BR exports (5), - .BR exportfs (8), - .BR rpc.rquotad (8), --.BR nfsstat (8). -+.BR nfsstat (8), -+.BR netconfig(5). - .SH AUTHOR - Olaf Kirch, Bill Hawes, H. J. Lu, G. Allan Morris III, - and a host of others. -diff --git a/utils/nfsd/nfssvc.c b/utils/nfsd/nfssvc.c -new file mode 100644 -index 0000000..ee862b2 ---- /dev/null -+++ b/utils/nfsd/nfssvc.c -@@ -0,0 +1,289 @@ -+/* -+ * utils/nfsd/nfssvc.c -+ * -+ * Run an NFS daemon. -+ * -+ * Copyright (C) 1995, 1996 Olaf Kirch -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include -+#endif -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "nfslib.h" -+#include "xlog.h" -+ -+#define NFSD_PORTS_FILE "/proc/fs/nfsd/portlist" -+#define NFSD_VERS_FILE "/proc/fs/nfsd/versions" -+#define NFSD_THREAD_FILE "/proc/fs/nfsd/threads" -+ -+/* -+ * declaring a common static scratch buffer here keeps us from having to -+ * continually thrash the stack. The value of 128 bytes here is really just a -+ * SWAG and can be increased if necessary. It ought to be enough for the -+ * routines below however. -+ */ -+char buf[128]; -+ -+/* -+ * Are there already sockets configured? If not, then it is safe to try to -+ * open some and pass them through. -+ * -+ * Note: If the user explicitly asked for 'udp', then we should probably check -+ * if that is open, and should open it if not. However we don't yet. All -+ * sockets have to be opened when the first daemon is started. -+ */ -+int -+nfssvc_inuse(void) -+{ -+ int fd, n; -+ -+ fd = open(NFSD_PORTS_FILE, O_RDONLY); -+ -+ /* problem opening file, assume that nothing is configured */ -+ if (fd < 0) -+ return 0; -+ -+ n = read(fd, buf, sizeof(buf)); -+ close(fd); -+ -+ xlog(D_GENERAL, "knfsd is currently %s", (n > 0) ? "up" : "down"); -+ -+ return (n > 0); -+} -+ -+static int -+nfssvc_setfds(const struct addrinfo *hints, const char *node, const char *port) -+{ -+ int fd, on = 1, fac = L_ERROR; -+ int sockfd = -1, rc = 0; -+ struct addrinfo *addrhead = NULL, *addr; -+ char *proto, *family; -+ -+ /* -+ * if file can't be opened, then assume that it's not available and -+ * that the caller should just fall back to the old nfsctl interface -+ */ -+ fd = open(NFSD_PORTS_FILE, O_WRONLY); -+ if (fd < 0) -+ return 0; -+ -+ switch(hints->ai_family) { -+ case AF_INET: -+ family = "inet"; -+ break; -+#ifdef IPV6_SUPPORTED -+ case AF_INET6: -+ family = "inet6"; -+ break; -+#endif /* IPV6_SUPPORTED */ -+ default: -+ xlog(L_ERROR, "Unknown address family specified: %d\n", -+ hints->ai_family); -+ rc = EAFNOSUPPORT; -+ goto error; -+ } -+ -+ rc = getaddrinfo(node, port, hints, &addrhead); -+ if (rc == EAI_NONAME && !strcmp(port, "nfs")) { -+ snprintf(buf, sizeof(buf), "%d", NFS_PORT); -+ rc = getaddrinfo(node, buf, hints, &addrhead); -+ } -+ -+ if (rc != 0) { -+ xlog(L_ERROR, "unable to resolve %s:%s to %s address: " -+ "%s", node ? node : "ANYADDR", port, family, -+ rc == EAI_SYSTEM ? strerror(errno) : -+ gai_strerror(rc)); -+ goto error; -+ } -+ -+ addr = addrhead; -+ while(addr) { -+ /* skip non-TCP / non-UDP sockets */ -+ switch(addr->ai_protocol) { -+ case IPPROTO_UDP: -+ proto = "UDP"; -+ break; -+ case IPPROTO_TCP: -+ proto = "TCP"; -+ break; -+ default: -+ addr = addr->ai_next; -+ continue; -+ } -+ -+ xlog(D_GENERAL, "Creating %s %s socket.", family, proto); -+ -+ /* open socket and prepare to hand it off to kernel */ -+ sockfd = socket(addr->ai_family, addr->ai_socktype, -+ addr->ai_protocol); -+ if (sockfd < 0) { -+ xlog(L_ERROR, "unable to create %s %s socket: " -+ "errno %d (%m)", family, proto, errno); -+ rc = errno; -+ goto error; -+ } -+#ifdef IPV6_SUPPORTED -+ if (addr->ai_family == AF_INET6 && -+ setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on))) { -+ xlog(L_ERROR, "unable to set IPV6_V6ONLY: " -+ "errno %d (%m)\n", errno); -+ rc = errno; -+ goto error; -+ } -+#endif /* IPV6_SUPPORTED */ -+ if (addr->ai_protocol == IPPROTO_TCP && -+ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) { -+ xlog(L_ERROR, "unable to set SO_REUSEADDR on %s " -+ "socket: errno %d (%m)", family, errno); -+ rc = errno; -+ goto error; -+ } -+ if (bind(sockfd, addr->ai_addr, addr->ai_addrlen)) { -+ xlog(L_ERROR, "unable to bind %s %s socket: " -+ "errno %d (%m)", family, proto, errno); -+ rc = errno; -+ goto error; -+ } -+ if (addr->ai_protocol == IPPROTO_TCP && listen(sockfd, 64)) { -+ xlog(L_ERROR, "unable to create listening socket: " -+ "errno %d (%m)", errno); -+ rc = errno; -+ goto error; -+ } -+ -+ if (fd < 0) -+ fd = open(NFSD_PORTS_FILE, O_WRONLY); -+ -+ if (fd < 0) { -+ xlog(L_ERROR, "couldn't open ports file: errno " -+ "%d (%m)", errno); -+ goto error; -+ } -+ -+ snprintf(buf, sizeof(buf), "%d\n", sockfd); -+ if (write(fd, buf, strlen(buf)) != strlen(buf)) { -+ /* -+ * this error may be common on older kernels that don't -+ * support IPv6, so turn into a debug message. -+ */ -+ if (errno == EAFNOSUPPORT) -+ fac = D_ALL; -+ xlog(fac, "writing fd to kernel failed: errno %d (%m)", -+ errno); -+ rc = errno; -+ goto error; -+ } -+ close(fd); -+ close(sockfd); -+ sockfd = fd = -1; -+ addr = addr->ai_next; -+ } -+error: -+ if (fd >= 0) -+ close(fd); -+ if (sockfd >= 0) -+ close(sockfd); -+ if (addrhead) -+ freeaddrinfo(addrhead); -+ return rc; -+} -+ -+int -+nfssvc_set_sockets(const int family, const unsigned int protobits, -+ const char *host, const char *port) -+{ -+ struct addrinfo hints = { .ai_flags = AI_PASSIVE | AI_ADDRCONFIG }; -+ -+ hints.ai_family = family; -+ -+ if (!NFSCTL_ANYPROTO(protobits)) -+ return EPROTOTYPE; -+ else if (!NFSCTL_UDPISSET(protobits)) -+ hints.ai_protocol = IPPROTO_TCP; -+ else if (!NFSCTL_TCPISSET(protobits)) -+ hints.ai_protocol = IPPROTO_UDP; -+ -+ return nfssvc_setfds(&hints, host, port); -+} -+ -+void -+nfssvc_setvers(unsigned int ctlbits, int minorvers4) -+{ -+ int fd, n, off; -+ char *ptr; -+ -+ ptr = buf; -+ off = 0; -+ fd = open(NFSD_VERS_FILE, O_WRONLY); -+ if (fd < 0) -+ return; -+ -+ for (n = NFSD_MINVERS; n <= NFSD_MAXVERS; n++) { -+ if (NFSCTL_VERISSET(ctlbits, n)) -+ off += snprintf(ptr+off, sizeof(buf) - off, "+%d ", n); -+ else -+ off += snprintf(ptr+off, sizeof(buf) - off, "-%d ", n); -+ } -+ n = minorvers4 >= 0 ? minorvers4 : -minorvers4; -+ if (n >= NFSD_MINMINORVERS4 && n <= NFSD_MAXMINORVERS4) -+ off += snprintf(ptr+off, sizeof(buf) - off, "%c4.%d", -+ minorvers4 > 0 ? '+' : '-', -+ n); -+ xlog(D_GENERAL, "Writing version string to kernel: %s", buf); -+ snprintf(ptr+off, sizeof(buf) - off, "\n"); -+ if (write(fd, buf, strlen(buf)) != strlen(buf)) -+ xlog(L_ERROR, "Setting version failed: errno %d (%m)", errno); -+ -+ close(fd); -+ -+ return; -+} -+ -+int -+nfssvc_threads(unsigned short port, const int nrservs) -+{ -+ struct nfsctl_arg arg; -+ struct servent *ent; -+ ssize_t n; -+ int fd; -+ -+ fd = open(NFSD_THREAD_FILE, O_WRONLY); -+ if (fd < 0) -+ fd = open("/proc/fs/nfs/threads", O_WRONLY); -+ if (fd >= 0) { -+ /* 2.5+ kernel with nfsd filesystem mounted. -+ * Just write the number of threads. -+ */ -+ snprintf(buf, sizeof(buf), "%d\n", nrservs); -+ n = write(fd, buf, strlen(buf)); -+ close(fd); -+ if (n != strlen(buf)) -+ return -1; -+ else -+ return 0; -+ } -+ -+ if (!port) { -+ ent = getservbyname("nfs", "udp"); -+ if (ent != NULL) -+ port = ntohs(ent->s_port); -+ else -+ port = NFS_PORT; -+ } -+ -+ arg.ca_version = NFSCTL_VERSION; -+ arg.ca_svc.svc_nthreads = nrservs; -+ arg.ca_svc.svc_port = port; -+ return nfsctl(NFSCTL_SVC, &arg, NULL); -+} -diff --git a/utils/nfsd/nfssvc.h b/utils/nfsd/nfssvc.h -new file mode 100644 -index 0000000..0c69bd6 ---- /dev/null -+++ b/utils/nfsd/nfssvc.h -@@ -0,0 +1,27 @@ -+/* -+ * utils/nfsd/nfssvc.h -- nfs service control routines for rpc.nfsd -+ * -+ * Copyright (C) 2009 Red Hat, Inc . -+ * Copyright (C) 2009 Jeff Layton -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version 2 -+ * of the License, or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License along -+ * with this program; if not, write to the Free Software Foundation, Inc., -+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -+ * -+ */ -+ -+int nfssvc_inuse(void); -+int nfssvc_set_sockets(const int family, const unsigned int protobits, -+ const char *host, const char *port); -+void nfssvc_setvers(unsigned int ctlbits, int minorvers4); -+int nfssvc_threads(unsigned short port, int nrservs); diff --git a/nfs-utils-1.2.1-rc4.patch b/nfs-utils-1.2.1-rc4.patch deleted file mode 100644 index 5cabcc5..0000000 --- a/nfs-utils-1.2.1-rc4.patch +++ /dev/null @@ -1,3601 +0,0 @@ -diff --git a/configure.ac b/configure.ac -index e0ca70e..1b653e4 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -136,6 +136,31 @@ AC_ARG_ENABLE(ipv6, - AC_SUBST(enable_ipv6) - AM_CONDITIONAL(CONFIG_IPV6, [test "$enable_ipv6" = "yes"]) - -+if test "$enable_mount" = yes; then -+ AC_ARG_ENABLE(mountconfig, -+ [AC_HELP_STRING([--enable-mountconfig], -+ [enable mount to use a configuration file])], -+ mountconfig=$enableval, -+ mountconfig=no) -+ if test "$enable_mountconfig" = yes; then -+ AC_DEFINE(MOUNT_CONFIG, 1, -+ [Define this if you want mount to read a configuration file]) -+ AC_ARG_WITH(mountfile, -+ [AC_HELP_STRING([--with-mountfile=filename], -+ [Using filename as the NFS mount options file [/etc/nfsmounts.conf]] -+ )], -+ mountfile=$withval, -+ mountfile=/etc/nfsmount.conf) -+ AC_SUBST(mountfile) -+ AC_DEFINE_UNQUOTED(MOUNTOPTS_CONFFILE, "$mountfile", -+ [This defines the location of the NFS mount configuration file]) -+ else -+ enable_mountconfig= -+ fi -+ AC_SUBST(enable_mountconfig) -+ AM_CONDITIONAL(MOUNT_CONFIG, [test "$enable_mountconfig" = "yes"]) -+fi -+ - dnl Check for TI-RPC library and headers - AC_LIBTIRPC - -diff --git a/support/include/Makefile.am b/support/include/Makefile.am -index 718abda..f5a77ec 100644 ---- a/support/include/Makefile.am -+++ b/support/include/Makefile.am -@@ -14,6 +14,7 @@ noinst_HEADERS = \ - xio.h \ - xlog.h \ - xmalloc.h \ -- xcommon.h -+ xcommon.h \ -+ conffile.h - - MAINTAINERCLEANFILES = Makefile.in -diff --git a/support/include/conffile.h b/support/include/conffile.h -new file mode 100644 -index 0000000..132a149 ---- /dev/null -+++ b/support/include/conffile.h -@@ -0,0 +1,77 @@ -+/* $OpenBSD: conf.h,v 1.30 2004/06/25 20:25:34 hshoexer Exp $ */ -+/* $EOM: conf.h,v 1.13 2000/09/18 00:01:47 ho Exp $ */ -+ -+/* -+ * Copyright (c) 1998, 1999, 2001 Niklas Hallqvist. All rights reserved. -+ * Copyright (c) 2000, 2003 H�kan Olsson. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+/* -+ * This code was written under funding by Ericsson Radio Systems. -+ */ -+ -+#ifndef _CONFFILE_H_ -+#define _CONFFILE_H_ -+ -+#include -+ -+struct conf_list_node { -+ TAILQ_ENTRY(conf_list_node) link; -+ char *field; -+}; -+ -+struct conf_list { -+ size_t cnt; -+ TAILQ_HEAD(conf_list_fields_head, conf_list_node) fields; -+}; -+ -+extern char *conf_path; -+ -+extern int conf_begin(void); -+extern int conf_decode_base64(u_int8_t *, u_int32_t *, u_char *); -+extern int conf_end(int, int); -+extern void conf_free_list(struct conf_list *); -+extern struct sockaddr *conf_get_address(char *, char *); -+extern struct conf_list *conf_get_list(char *, char *); -+extern struct conf_list *conf_get_tag_list(char *); -+extern int conf_get_num(char *, char *, int); -+extern char *conf_get_str(char *, char *); -+extern char *conf_get_section(char *, char *, char *); -+extern void conf_init(void); -+extern int conf_match_num(char *, char *, int); -+extern void conf_reinit(void); -+extern int conf_remove(int, char *, char *); -+extern int conf_remove_section(int, char *); -+extern void conf_report(void); -+ -+/* -+ * Convert letter from upper case to lower case -+ */ -+static inline void upper2lower(char *str) -+{ -+ char c; -+ -+ while ((c = tolower(*str))) -+ *str++ = c; -+} -+#endif /* _CONFFILE_H_ */ -diff --git a/support/nfs/Makefile.am b/support/nfs/Makefile.am -index 096f56d..e9462fc 100644 ---- a/support/nfs/Makefile.am -+++ b/support/nfs/Makefile.am -@@ -4,7 +4,7 @@ noinst_LIBRARIES = libnfs.a - libnfs_a_SOURCES = exports.c rmtab.c xio.c rpcmisc.c rpcdispatch.c \ - xlog.c xcommon.c wildmat.c nfsclient.c \ - nfsexport.c getfh.c nfsctl.c rpc_socket.c getport.c \ -- svc_socket.c cacheio.c closeall.c nfs_mntent.c -+ svc_socket.c cacheio.c closeall.c nfs_mntent.c conffile.c - - MAINTAINERCLEANFILES = Makefile.in - -diff --git a/support/nfs/conffile.c b/support/nfs/conffile.c -new file mode 100644 -index 0000000..b277c2a ---- /dev/null -+++ b/support/nfs/conffile.c -@@ -0,0 +1,962 @@ -+/* $OpenBSD: conf.c,v 1.55 2003/06/03 14:28:16 ho Exp $ */ -+/* $EOM: conf.c,v 1.48 2000/12/04 02:04:29 angelos Exp $ */ -+ -+/* -+ * Copyright (c) 1998, 1999, 2000, 2001 Niklas Hallqvist. All rights reserved. -+ * Copyright (c) 2000, 2001, 2002 H�kan Olsson. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+/* -+ * This code was written under funding by Ericsson Radio Systems. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "conffile.h" -+#include "xlog.h" -+ -+static void conf_load_defaults (int); -+static int conf_set(int , char *, char *, char *, -+ char *, int , int ); -+ -+struct conf_trans { -+ TAILQ_ENTRY (conf_trans) link; -+ int trans; -+ enum conf_op { CONF_SET, CONF_REMOVE, CONF_REMOVE_SECTION } op; -+ char *section; -+ char *arg; -+ char *tag; -+ char *value; -+ int override; -+ int is_default; -+}; -+ -+TAILQ_HEAD (conf_trans_head, conf_trans) conf_trans_queue; -+ -+/* -+ * Radix-64 Encoding. -+ */ -+static const u_int8_t bin2asc[] -+ = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -+ -+static const u_int8_t asc2bin[] = -+{ -+ 255, 255, 255, 255, 255, 255, 255, 255, -+ 255, 255, 255, 255, 255, 255, 255, 255, -+ 255, 255, 255, 255, 255, 255, 255, 255, -+ 255, 255, 255, 255, 255, 255, 255, 255, -+ 255, 255, 255, 255, 255, 255, 255, 255, -+ 255, 255, 255, 62, 255, 255, 255, 63, -+ 52, 53, 54, 55, 56, 57, 58, 59, -+ 60, 61, 255, 255, 255, 255, 255, 255, -+ 255, 0, 1, 2, 3, 4, 5, 6, -+ 7, 8, 9, 10, 11, 12, 13, 14, -+ 15, 16, 17, 18, 19, 20, 21, 22, -+ 23, 24, 25, 255, 255, 255, 255, 255, -+ 255, 26, 27, 28, 29, 30, 31, 32, -+ 33, 34, 35, 36, 37, 38, 39, 40, -+ 41, 42, 43, 44, 45, 46, 47, 48, -+ 49, 50, 51, 255, 255, 255, 255, 255 -+}; -+ -+struct conf_binding { -+ LIST_ENTRY (conf_binding) link; -+ char *section; -+ char *arg; -+ char *tag; -+ char *value; -+ int is_default; -+}; -+ -+char *conf_path; -+LIST_HEAD (conf_bindings, conf_binding) conf_bindings[256]; -+ -+static char *conf_addr; -+ -+static __inline__ u_int8_t -+conf_hash(char *s) -+{ -+ u_int8_t hash = 0; -+ -+ while (*s) { -+ hash = ((hash << 1) | (hash >> 7)) ^ tolower (*s); -+ s++; -+ } -+ return hash; -+} -+ -+/* -+ * Insert a tag-value combination from LINE (the equal sign is at POS) -+ */ -+static int -+conf_remove_now(char *section, char *tag) -+{ -+ struct conf_binding *cb, *next; -+ -+ cb = LIST_FIRST(&conf_bindings[conf_hash (section)]); -+ for (; cb; cb = next) { -+ next = LIST_NEXT(cb, link); -+ if (strcasecmp(cb->section, section) == 0 -+ && strcasecmp(cb->tag, tag) == 0) { -+ LIST_REMOVE(cb, link); -+ xlog(LOG_INFO,"[%s]:%s->%s removed", section, tag, cb->value); -+ free(cb->section); -+ free(cb->arg); -+ free(cb->tag); -+ free(cb->value); -+ free(cb); -+ return 0; -+ } -+ } -+ return 1; -+} -+ -+static int -+conf_remove_section_now(char *section) -+{ -+ struct conf_binding *cb, *next; -+ int unseen = 1; -+ -+ cb = LIST_FIRST(&conf_bindings[conf_hash (section)]); -+ for (; cb; cb = next) { -+ next = LIST_NEXT(cb, link); -+ if (strcasecmp(cb->section, section) == 0) { -+ unseen = 0; -+ LIST_REMOVE(cb, link); -+ xlog(LOG_INFO, "[%s]:%s->%s removed", section, cb->tag, cb->value); -+ free(cb->section); -+ free(cb->arg); -+ free(cb->tag); -+ free(cb->value); -+ free(cb); -+ } -+ } -+ return unseen; -+} -+ -+/* -+ * Insert a tag-value combination from LINE (the equal sign is at POS) -+ * into SECTION of our configuration database. -+ */ -+static int -+conf_set_now(char *section, char *arg, char *tag, -+ char *value, int override, int is_default) -+{ -+ struct conf_binding *node = 0; -+ -+ if (override) -+ conf_remove_now(section, tag); -+ else if (conf_get_section(section, arg, tag)) { -+ if (!is_default) { -+ xlog(LOG_INFO, "conf_set: duplicate tag [%s]:%s, ignoring...\n", -+ section, tag); -+ } -+ return 1; -+ } -+ node = calloc(1, sizeof *node); -+ if (!node) { -+ xlog_warn("conf_set: calloc (1, %lu) failed", (unsigned long)sizeof *node); -+ return 1; -+ } -+ node->section = strdup(section); -+ if (arg) -+ node->arg = strdup(arg); -+ node->tag = strdup(tag); -+ node->value = strdup(value); -+ node->is_default = is_default; -+ -+ LIST_INSERT_HEAD(&conf_bindings[conf_hash (section)], node, link); -+ return 0; -+} -+ -+/* -+ * Parse the line LINE of SZ bytes. Skip Comments, recognize section -+ * headers and feed tag-value pairs into our configuration database. -+ */ -+static void -+conf_parse_line(int trans, char *line, size_t sz) -+{ -+ char *val, *ptr; -+ size_t i; -+ int j; -+ static char *section = 0; -+ static char *arg = 0; -+ static int ln = 0; -+ -+ /* Lines starting with '#' or ';' are comments. */ -+ ln++; -+ /* Ignore blank lines */ -+ if (*line == '\0') -+ return; -+ -+ /* Strip off any leading blanks */ -+ while (isblank(*line)) -+ line++; -+ -+ if (*line == '#' || *line == ';') -+ return; -+ -+ /* '[section]' parsing... */ -+ if (*line == '[') { -+ line++; -+ /* Strip off any blanks after '[' */ -+ while (isblank(*line)) -+ line++; -+ for (i = 0; i < sz; i++) { -+ if (line[i] == ']') { -+ break; -+ } -+ } -+ if (section) -+ free(section); -+ if (i == sz) { -+ xlog_warn("config file error: line %d: " -+ "non-matched ']', ignoring until next section", ln); -+ section = 0; -+ return; -+ } -+ /* Strip off any blanks before ']' */ -+ val = line; -+ while (*val && !isblank(*val)) -+ val++, j++; -+ if (*val) -+ i = j; -+ section = malloc(i); -+ if (!section) { -+ xlog_warn("conf_parse_line: %d: malloc (%lu) failed", ln, -+ (unsigned long)i); -+ return; -+ } -+ strncpy(section, line, i); -+ -+ if (arg) -+ free(arg); -+ arg = 0; -+ -+ ptr = strchr(val, '"'); -+ if (ptr == NULL) -+ return; -+ line = ++ptr; -+ while (*ptr && *ptr != '"') -+ ptr++; -+ if (*ptr == '\0') { -+ xlog_warn("config file error: line %d: " -+ "non-matched '\"', ignoring until next section", ln); -+ } else { -+ *ptr = '\0'; -+ arg = strdup(line); -+ if (!arg) -+ xlog_warn("conf_parse_line: %d: malloc arg failed", ln); -+ } -+ return; -+ } -+ -+ /* Deal with assignments. */ -+ for (i = 0; i < sz; i++) { -+ if (line[i] == '=') { -+ /* If no section, we are ignoring the lines. */ -+ if (!section) { -+ xlog_warn("config file error: line %d: " -+ "ignoring line due to no section", ln); -+ return; -+ } -+ line[strcspn (line, " \t=")] = '\0'; -+ val = line + i + 1 + strspn (line + i + 1, " \t"); -+ -+ /* Skip trailing comments, if any */ -+ for (j = 0; j < sz - (val - line); j++) { -+ if (val[j] == '#' || val[j] == ';') { -+ val[j] = '\0'; -+ break; -+ } -+ } -+ -+ /* Skip trailing whitespace, if any */ -+ for (j--; j > 0; j--) { -+ if (isspace(val[j])) -+ val[j] = '\0'; -+ else -+ break; -+ } -+ -+ /* XXX Perhaps should we not ignore errors? */ -+ conf_set(trans, section, arg, line, val, 0, 0); -+ return; -+ } -+ } -+ /* Other non-empty lines are weird. */ -+ i = strspn(line, " \t"); -+ if (line[i]) -+ xlog_warn("config file error: line %d:", ln); -+ -+ return; -+} -+ -+/* Parse the mapped configuration file. */ -+static void -+conf_parse(int trans, char *buf, size_t sz) -+{ -+ char *cp = buf; -+ char *bufend = buf + sz; -+ char *line; -+ -+ line = cp; -+ while (cp < bufend) { -+ if (*cp == '\n') { -+ /* Check for escaped newlines. */ -+ if (cp > buf && *(cp - 1) == '\\') -+ *(cp - 1) = *cp = ' '; -+ else { -+ *cp = '\0'; -+ conf_parse_line(trans, line, cp - line); -+ line = cp + 1; -+ } -+ } -+ cp++; -+ } -+ if (cp != line) -+ xlog_warn("conf_parse: last line non-terminated, ignored."); -+} -+ -+static void -+conf_load_defaults(int tr) -+{ -+ /* No defaults */ -+ return; -+} -+ -+void -+conf_init (void) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++) -+ LIST_INIT (&conf_bindings[i]); -+ -+ TAILQ_INIT (&conf_trans_queue); -+ conf_reinit(); -+} -+ -+/* Open the config file and map it into our address space, then parse it. */ -+void -+conf_reinit(void) -+{ -+ struct conf_binding *cb = 0; -+ int fd, trans; -+ unsigned int i; -+ size_t sz; -+ char *new_conf_addr = 0; -+ struct stat sb; -+ -+ if ((stat (conf_path, &sb) == 0) || (errno != ENOENT)) { -+ sz = sb.st_size; -+ fd = open (conf_path, O_RDONLY, 0); -+ if (fd == -1) { -+ xlog_warn("conf_reinit: open (\"%s\", O_RDONLY) failed", conf_path); -+ return; -+ } -+ -+ new_conf_addr = malloc(sz); -+ if (!new_conf_addr) { -+ xlog_warn("conf_reinit: malloc (%lu) failed", (unsigned long)sz); -+ goto fail; -+ } -+ -+ /* XXX I assume short reads won't happen here. */ -+ if (read (fd, new_conf_addr, sz) != (int)sz) { -+ xlog_warn("conf_reinit: read (%d, %p, %lu) failed", -+ fd, new_conf_addr, (unsigned long)sz); -+ goto fail; -+ } -+ close(fd); -+ -+ trans = conf_begin(); -+ /* XXX Should we not care about errors and rollback? */ -+ conf_parse(trans, new_conf_addr, sz); -+ } -+ else -+ trans = conf_begin(); -+ -+ /* Load default configuration values. */ -+ conf_load_defaults(trans); -+ -+ /* Free potential existing configuration. */ -+ if (conf_addr) { -+ for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++) { -+ cb = LIST_FIRST (&conf_bindings[i]); -+ for (; cb; cb = LIST_FIRST (&conf_bindings[i])) -+ conf_remove_now(cb->section, cb->tag); -+ } -+ free (conf_addr); -+ } -+ -+ conf_end(trans, 1); -+ conf_addr = new_conf_addr; -+ return; -+ -+fail: -+ if (new_conf_addr) -+ free(new_conf_addr); -+ close (fd); -+} -+ -+/* -+ * Return the numeric value denoted by TAG in section SECTION or DEF -+ * if that tag does not exist. -+ */ -+int -+conf_get_num(char *section, char *tag, int def) -+{ -+ char *value = conf_get_str(section, tag); -+ -+ if (value) -+ return atoi(value); -+ -+ return def; -+} -+ -+/* Validate X according to the range denoted by TAG in section SECTION. */ -+int -+conf_match_num(char *section, char *tag, int x) -+{ -+ char *value = conf_get_str (section, tag); -+ int val, min, max, n; -+ -+ if (!value) -+ return 0; -+ n = sscanf (value, "%d,%d:%d", &val, &min, &max); -+ switch (n) { -+ case 1: -+ xlog(LOG_INFO, "conf_match_num: %s:%s %d==%d?", section, tag, val, x); -+ return x == val; -+ case 3: -+ xlog(LOG_INFO, "conf_match_num: %s:%s %d<=%d<=%d?", section, -+ tag, min, x, max); -+ return min <= x && max >= x; -+ default: -+ xlog(LOG_INFO, "conf_match_num: section %s tag %s: invalid number spec %s", -+ section, tag, value); -+ } -+ return 0; -+} -+ -+/* Return the string value denoted by TAG in section SECTION. */ -+char * -+conf_get_str(char *section, char *tag) -+{ -+ struct conf_binding *cb; -+ -+ cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); -+ for (; cb; cb = LIST_NEXT (cb, link)) { -+ if (strcasecmp (section, cb->section) == 0 -+ && strcasecmp (tag, cb->tag) == 0) -+ return cb->value; -+ } -+ return 0; -+} -+/* -+ * Find a section that may or may not have an argument -+ */ -+char * -+conf_get_section(char *section, char *arg, char *tag) -+{ -+ struct conf_binding *cb; -+ -+ cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); -+ for (; cb; cb = LIST_NEXT (cb, link)) { -+ if (strcasecmp(section, cb->section) != 0) -+ continue; -+ if (arg && strcasecmp(arg, cb->arg) != 0) -+ continue; -+ if (strcasecmp(tag, cb->tag) != 0) -+ continue; -+ return cb->value; -+ } -+ return 0; -+} -+ -+/* -+ * Build a list of string values out of the comma separated value denoted by -+ * TAG in SECTION. -+ */ -+struct conf_list * -+conf_get_list(char *section, char *tag) -+{ -+ char *liststr = 0, *p, *field, *t; -+ struct conf_list *list = 0; -+ struct conf_list_node *node; -+ -+ list = malloc (sizeof *list); -+ if (!list) -+ goto cleanup; -+ TAILQ_INIT (&list->fields); -+ list->cnt = 0; -+ liststr = conf_get_str(section, tag); -+ if (!liststr) -+ goto cleanup; -+ liststr = strdup (liststr); -+ if (!liststr) -+ goto cleanup; -+ p = liststr; -+ while ((field = strsep (&p, ",")) != NULL) { -+ /* Skip leading whitespace */ -+ while (isspace (*field)) -+ field++; -+ /* Skip trailing whitespace */ -+ if (p) { -+ for (t = p - 1; t > field && isspace (*t); t--) -+ *t = '\0'; -+ } -+ if (*field == '\0') { -+ xlog(LOG_INFO, "conf_get_list: empty field, ignoring..."); -+ continue; -+ } -+ list->cnt++; -+ node = calloc (1, sizeof *node); -+ if (!node) -+ goto cleanup; -+ node->field = strdup (field); -+ if (!node->field) { -+ free(node); -+ goto cleanup; -+ } -+ TAILQ_INSERT_TAIL (&list->fields, node, link); -+ } -+ free (liststr); -+ return list; -+ -+cleanup: -+ if (list) -+ conf_free_list(list); -+ if (liststr) -+ free(liststr); -+ return 0; -+} -+ -+struct conf_list * -+conf_get_tag_list(char *section) -+{ -+ struct conf_list *list = 0; -+ struct conf_list_node *node; -+ struct conf_binding *cb; -+ -+ list = malloc(sizeof *list); -+ if (!list) -+ goto cleanup; -+ TAILQ_INIT(&list->fields); -+ list->cnt = 0; -+ cb = LIST_FIRST(&conf_bindings[conf_hash (section)]); -+ for (; cb; cb = LIST_NEXT(cb, link)) { -+ if (strcasecmp (section, cb->section) == 0) { -+ list->cnt++; -+ node = calloc(1, sizeof *node); -+ if (!node) -+ goto cleanup; -+ node->field = strdup(cb->tag); -+ if (!node->field) { -+ free(node); -+ goto cleanup; -+ } -+ TAILQ_INSERT_TAIL(&list->fields, node, link); -+ } -+ } -+ return list; -+ -+cleanup: -+ if (list) -+ conf_free_list(list); -+ return 0; -+} -+ -+/* Decode a PEM encoded buffer. */ -+int -+conf_decode_base64 (u_int8_t *out, u_int32_t *len, u_char *buf) -+{ -+ u_int32_t c = 0; -+ u_int8_t c1, c2, c3, c4; -+ -+ while (*buf) { -+ if (*buf > 127 || (c1 = asc2bin[*buf]) == 255) -+ return 0; -+ -+ buf++; -+ if (*buf > 127 || (c2 = asc2bin[*buf]) == 255) -+ return 0; -+ -+ buf++; -+ if (*buf == '=') { -+ c3 = c4 = 0; -+ c++; -+ -+ /* Check last four bit */ -+ if (c2 & 0xF) -+ return 0; -+ -+ if (strcmp((char *)buf, "==") == 0) -+ buf++; -+ else -+ return 0; -+ } else if (*buf > 127 || (c3 = asc2bin[*buf]) == 255) -+ return 0; -+ else { -+ if (*++buf == '=') { -+ c4 = 0; -+ c += 2; -+ -+ /* Check last two bit */ -+ if (c3 & 3) -+ return 0; -+ -+ if (strcmp((char *)buf, "=")) -+ return 0; -+ } else if (*buf > 127 || (c4 = asc2bin[*buf]) == 255) -+ return 0; -+ else -+ c += 3; -+ } -+ -+ buf++; -+ *out++ = (c1 << 2) | (c2 >> 4); -+ *out++ = (c2 << 4) | (c3 >> 2); -+ *out++ = (c3 << 6) | c4; -+ } -+ -+ *len = c; -+ return 1; -+} -+ -+void -+conf_free_list(struct conf_list *list) -+{ -+ struct conf_list_node *node = TAILQ_FIRST(&list->fields); -+ -+ while (node) { -+ TAILQ_REMOVE(&list->fields, node, link); -+ if (node->field) -+ free(node->field); -+ free (node); -+ node = TAILQ_FIRST(&list->fields); -+ } -+ free (list); -+} -+ -+int -+conf_begin(void) -+{ -+ static int seq = 0; -+ -+ return ++seq; -+} -+ -+static struct conf_trans * -+conf_trans_node(int transaction, enum conf_op op) -+{ -+ struct conf_trans *node; -+ -+ node = calloc (1, sizeof *node); -+ if (!node) { -+ xlog_warn("conf_trans_node: calloc (1, %lu) failed", -+ (unsigned long)sizeof *node); -+ return 0; -+ } -+ node->trans = transaction; -+ node->op = op; -+ TAILQ_INSERT_TAIL (&conf_trans_queue, node, link); -+ return node; -+} -+ -+/* Queue a set operation. */ -+static int -+conf_set(int transaction, char *section, char *arg, -+ char *tag, char *value, int override, int is_default) -+{ -+ struct conf_trans *node; -+ -+ node = conf_trans_node(transaction, CONF_SET); -+ if (!node) -+ return 1; -+ node->section = strdup(section); -+ if (!node->section) { -+ xlog_warn("conf_set: strdup(\"%s\") failed", section); -+ goto fail; -+ } -+ /* Make Section names case-insensitive */ -+ upper2lower(node->section); -+ -+ if (arg) { -+ node->arg = strdup(arg); -+ if (!node->arg) { -+ xlog_warn("conf_set: strdup(\"%s\") failed", arg); -+ goto fail; -+ } -+ } else -+ node->arg = NULL; -+ -+ node->tag = strdup(tag); -+ if (!node->tag) { -+ xlog_warn("conf_set: strdup(\"%s\") failed", tag); -+ goto fail; -+ } -+ node->value = strdup(value); -+ if (!node->value) { -+ xlog_warn("conf_set: strdup(\"%s\") failed", value); -+ goto fail; -+ } -+ node->override = override; -+ node->is_default = is_default; -+ return 0; -+ -+fail: -+ if (node->tag) -+ free(node->tag); -+ if (node->section) -+ free(node->section); -+ if (node) -+ free(node); -+ return 1; -+} -+ -+/* Queue a remove operation. */ -+int -+conf_remove(int transaction, char *section, char *tag) -+{ -+ struct conf_trans *node; -+ -+ node = conf_trans_node(transaction, CONF_REMOVE); -+ if (!node) -+ goto fail; -+ node->section = strdup(section); -+ if (!node->section) { -+ xlog_warn("conf_remove: strdup(\"%s\") failed", section); -+ goto fail; -+ } -+ node->tag = strdup(tag); -+ if (!node->tag) { -+ xlog_warn("conf_remove: strdup(\"%s\") failed", tag); -+ goto fail; -+ } -+ return 0; -+ -+fail: -+ if (node && node->section) -+ free (node->section); -+ if (node) -+ free (node); -+ return 1; -+} -+ -+/* Queue a remove section operation. */ -+int -+conf_remove_section(int transaction, char *section) -+{ -+ struct conf_trans *node; -+ -+ node = conf_trans_node(transaction, CONF_REMOVE_SECTION); -+ if (!node) -+ goto fail; -+ node->section = strdup(section); -+ if (!node->section) { -+ xlog_warn("conf_remove_section: strdup(\"%s\") failed", section); -+ goto fail; -+ } -+ return 0; -+ -+fail: -+ if (node) -+ free(node); -+ return 1; -+} -+ -+/* Execute all queued operations for this transaction. Cleanup. */ -+int -+conf_end(int transaction, int commit) -+{ -+ struct conf_trans *node, *next; -+ -+ for (node = TAILQ_FIRST(&conf_trans_queue); node; node = next) { -+ next = TAILQ_NEXT(node, link); -+ if (node->trans == transaction) { -+ if (commit) { -+ switch (node->op) { -+ case CONF_SET: -+ conf_set_now(node->section, node->arg, -+ node->tag, node->value, node->override, -+ node->is_default); -+ break; -+ case CONF_REMOVE: -+ conf_remove_now(node->section, node->tag); -+ break; -+ case CONF_REMOVE_SECTION: -+ conf_remove_section_now(node->section); -+ break; -+ default: -+ xlog(LOG_INFO, "conf_end: unknown operation: %d", node->op); -+ } -+ } -+ TAILQ_REMOVE (&conf_trans_queue, node, link); -+ if (node->section) -+ free(node->section); -+ if (node->tag) -+ free(node->tag); -+ if (node->value) -+ free(node->value); -+ free (node); -+ } -+ } -+ return 0; -+} -+ -+/* -+ * Dump running configuration upon SIGUSR1. -+ * Configuration is "stored in reverse order", so reverse it again. -+ */ -+struct dumper { -+ char *s, *v; -+ struct dumper *next; -+}; -+ -+static void -+conf_report_dump(struct dumper *node) -+{ -+ /* Recursive, cleanup when we're done. */ -+ if (node->next) -+ conf_report_dump(node->next); -+ -+ if (node->v) -+ xlog(LOG_INFO, "%s=\t%s", node->s, node->v); -+ else if (node->s) { -+ xlog(LOG_INFO, "%s", node->s); -+ if (strlen(node->s) > 0) -+ free(node->s); -+ } -+ -+ free (node); -+} -+ -+void -+conf_report (void) -+{ -+ struct conf_binding *cb, *last = 0; -+ unsigned int i, len, diff_arg = 0; -+ char *current_section = (char *)0; -+ char *current_arg = (char *)0; -+ struct dumper *dumper, *dnode; -+ -+ dumper = dnode = (struct dumper *)calloc(1, sizeof *dumper); -+ if (!dumper) -+ goto mem_fail; -+ -+ xlog(LOG_INFO, "conf_report: dumping running configuration"); -+ -+ for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++) -+ for (cb = LIST_FIRST(&conf_bindings[i]); cb; cb = LIST_NEXT(cb, link)) { -+ if (!cb->is_default) { -+ /* Make sure the Section arugment is the same */ -+ if (current_arg && current_section && cb->arg) { -+ if (strcmp(cb->section, current_section) == 0 && -+ strcmp(cb->arg, current_arg) != 0) -+ diff_arg = 1; -+ } -+ /* Dump this entry. */ -+ if (!current_section || strcmp(cb->section, current_section) -+ || diff_arg) { -+ if (current_section || diff_arg) { -+ len = strlen (current_section) + 3; -+ if (current_arg) -+ len += strlen(current_arg) + 3; -+ dnode->s = malloc(len); -+ if (!dnode->s) -+ goto mem_fail; -+ -+ if (current_arg) -+ snprintf(dnode->s, len, "[%s \"%s\"]", -+ current_section, current_arg); -+ else -+ snprintf(dnode->s, len, "[%s]", current_section); -+ -+ dnode->next = -+ (struct dumper *)calloc(1, sizeof (struct dumper)); -+ dnode = dnode->next; -+ if (!dnode) -+ goto mem_fail; -+ -+ dnode->s = ""; -+ dnode->next = -+ (struct dumper *)calloc(1, sizeof (struct dumper)); -+ dnode = dnode->next; -+ if (!dnode) -+ goto mem_fail; -+ } -+ current_section = cb->section; -+ current_arg = cb->arg; -+ diff_arg = 0; -+ } -+ dnode->s = cb->tag; -+ dnode->v = cb->value; -+ dnode->next = (struct dumper *)calloc (1, sizeof (struct dumper)); -+ dnode = dnode->next; -+ if (!dnode) -+ goto mem_fail; -+ last = cb; -+ } -+ } -+ -+ if (last) { -+ len = strlen(last->section) + 3; -+ if (last->arg) -+ len += strlen(last->arg) + 3; -+ dnode->s = malloc(len); -+ if (!dnode->s) -+ goto mem_fail; -+ if (last->arg) -+ snprintf(dnode->s, len, "[%s \"%s\"]", last->section, last->arg); -+ else -+ snprintf(dnode->s, len, "[%s]", last->section); -+ } -+ conf_report_dump(dumper); -+ return; -+ -+mem_fail: -+ xlog_warn("conf_report: malloc/calloc failed"); -+ while ((dnode = dumper) != 0) { -+ dumper = dumper->next; -+ if (dnode->s) -+ free(dnode->s); -+ free(dnode); -+ } -+ return; -+} -diff --git a/utils/idmapd/Makefile.am b/utils/idmapd/Makefile.am -index eb393df..4dabb3d 100644 ---- a/utils/idmapd/Makefile.am -+++ b/utils/idmapd/Makefile.am -@@ -14,7 +14,6 @@ EXTRA_DIST = \ - - idmapd_SOURCES = \ - atomicio.c \ -- cfg.c \ - idmapd.c \ - strlcat.c \ - strlcpy.c \ -diff --git a/utils/idmapd/cfg.c b/utils/idmapd/cfg.c -deleted file mode 100644 -index 16d392a..0000000 ---- a/utils/idmapd/cfg.c -+++ /dev/null -@@ -1,893 +0,0 @@ --/* $OpenBSD: conf.c,v 1.55 2003/06/03 14:28:16 ho Exp $ */ --/* $EOM: conf.c,v 1.48 2000/12/04 02:04:29 angelos Exp $ */ -- --/* -- * Copyright (c) 1998, 1999, 2000, 2001 Niklas Hallqvist. All rights reserved. -- * Copyright (c) 2000, 2001, 2002 H�kan Olsson. All rights reserved. -- * -- * Redistribution and use in source and binary forms, with or without -- * modification, are permitted provided that the following conditions -- * are met: -- * 1. Redistributions of source code must retain the above copyright -- * notice, this list of conditions and the following disclaimer. -- * 2. Redistributions in binary form must reproduce the above copyright -- * notice, this list of conditions and the following disclaimer in the -- * documentation and/or other materials provided with the distribution. -- * -- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- */ -- --/* -- * This code was written under funding by Ericsson Radio Systems. -- */ -- --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#include "cfg.h" -- --static void conf_load_defaults (int); --#if 0 --static int conf_find_trans_xf (int, char *); --#endif -- --size_t strlcpy(char *, const char *, size_t); -- --struct conf_trans { -- TAILQ_ENTRY (conf_trans) link; -- int trans; -- enum conf_op { CONF_SET, CONF_REMOVE, CONF_REMOVE_SECTION } op; -- char *section; -- char *tag; -- char *value; -- int override; -- int is_default; --}; -- --TAILQ_HEAD (conf_trans_head, conf_trans) conf_trans_queue; -- --/* -- * Radix-64 Encoding. -- */ --const u_int8_t bin2asc[] -- = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -- --const u_int8_t asc2bin[] = --{ -- 255, 255, 255, 255, 255, 255, 255, 255, -- 255, 255, 255, 255, 255, 255, 255, 255, -- 255, 255, 255, 255, 255, 255, 255, 255, -- 255, 255, 255, 255, 255, 255, 255, 255, -- 255, 255, 255, 255, 255, 255, 255, 255, -- 255, 255, 255, 62, 255, 255, 255, 63, -- 52, 53, 54, 55, 56, 57, 58, 59, -- 60, 61, 255, 255, 255, 255, 255, 255, -- 255, 0, 1, 2, 3, 4, 5, 6, -- 7, 8, 9, 10, 11, 12, 13, 14, -- 15, 16, 17, 18, 19, 20, 21, 22, -- 23, 24, 25, 255, 255, 255, 255, 255, -- 255, 26, 27, 28, 29, 30, 31, 32, -- 33, 34, 35, 36, 37, 38, 39, 40, -- 41, 42, 43, 44, 45, 46, 47, 48, -- 49, 50, 51, 255, 255, 255, 255, 255 --}; -- --struct conf_binding { -- LIST_ENTRY (conf_binding) link; -- char *section; -- char *tag; -- char *value; -- int is_default; --}; -- --char *conf_path; --LIST_HEAD (conf_bindings, conf_binding) conf_bindings[256]; -- --static char *conf_addr; -- --static __inline__ u_int8_t --conf_hash (char *s) --{ -- u_int8_t hash = 0; -- -- while (*s) -- { -- hash = ((hash << 1) | (hash >> 7)) ^ tolower (*s); -- s++; -- } -- return hash; --} -- --/* -- * Insert a tag-value combination from LINE (the equal sign is at POS) -- */ --static int --conf_remove_now (char *section, char *tag) --{ -- struct conf_binding *cb, *next; -- -- for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb; cb = next) -- { -- next = LIST_NEXT (cb, link); -- if (strcasecmp (cb->section, section) == 0 -- && strcasecmp (cb->tag, tag) == 0) -- { -- LIST_REMOVE (cb, link); -- warnx("[%s]:%s->%s removed", section, tag, cb->value); -- free (cb->section); -- free (cb->tag); -- free (cb->value); -- free (cb); -- return 0; -- } -- } -- return 1; --} -- --static int --conf_remove_section_now (char *section) --{ -- struct conf_binding *cb, *next; -- int unseen = 1; -- -- for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb; cb = next) -- { -- next = LIST_NEXT (cb, link); -- if (strcasecmp (cb->section, section) == 0) -- { -- unseen = 0; -- LIST_REMOVE (cb, link); -- warnx("[%s]:%s->%s removed", section, cb->tag, cb->value); -- free (cb->section); -- free (cb->tag); -- free (cb->value); -- free (cb); -- } -- } -- return unseen; --} -- --/* -- * Insert a tag-value combination from LINE (the equal sign is at POS) -- * into SECTION of our configuration database. -- */ --static int --conf_set_now (char *section, char *tag, char *value, int override, -- int is_default) --{ -- struct conf_binding *node = 0; -- -- if (override) -- conf_remove_now (section, tag); -- else if (conf_get_str (section, tag)) -- { -- if (!is_default) -- warnx("conf_set: duplicate tag [%s]:%s, ignoring...\n", section, tag); -- return 1; -- } -- -- node = calloc (1, sizeof *node); -- if (!node) -- { -- warnx("conf_set: calloc (1, %lu) failed", (unsigned long)sizeof *node); -- return 1; -- } -- node->section = strdup (section); -- node->tag = strdup (tag); -- node->value = strdup (value); -- node->is_default = is_default; -- -- LIST_INSERT_HEAD (&conf_bindings[conf_hash (section)], node, link); -- return 0; --} -- --/* -- * Parse the line LINE of SZ bytes. Skip Comments, recognize section -- * headers and feed tag-value pairs into our configuration database. -- */ --static void --conf_parse_line (int trans, char *line, size_t sz) --{ -- char *val; -- size_t i; -- int j; -- static char *section = 0; -- static int ln = 0; -- -- ln++; -- -- /* Lines starting with '#' or ';' are comments. */ -- if (*line == '#' || *line == ';') -- return; -- -- /* '[section]' parsing... */ -- if (*line == '[') -- { -- for (i = 1; i < sz; i++) -- if (line[i] == ']') -- break; -- if (section) -- free (section); -- if (i == sz) -- { -- warnx("conf_parse_line: %d:" -- "non-matched ']', ignoring until next section", ln); -- section = 0; -- return; -- } -- section = malloc (i); -- if (!section) -- { -- warnx("conf_parse_line: %d: malloc (%lu) failed", ln, -- (unsigned long)i); -- return; -- } -- strlcpy (section, line + 1, i); -- return; -- } -- -- /* Deal with assignments. */ -- for (i = 0; i < sz; i++) -- if (line[i] == '=') -- { -- /* If no section, we are ignoring the lines. */ -- if (!section) -- { -- warnx("conf_parse_line: %d: ignoring line due to no section", ln); -- return; -- } -- line[strcspn (line, " \t=")] = '\0'; -- val = line + i + 1 + strspn (line + i + 1, " \t"); -- /* Skip trailing whitespace, if any */ -- for (j = sz - (val - line) - 1; j > 0 && isspace (val[j]); j--) -- val[j] = '\0'; -- /* XXX Perhaps should we not ignore errors? */ -- conf_set (trans, section, line, val, 0, 0); -- return; -- } -- -- /* Other non-empty lines are weird. */ -- i = strspn (line, " \t"); -- if (line[i]) -- warnx("conf_parse_line: %d: syntax error", ln); -- -- return; --} -- --/* Parse the mapped configuration file. */ --static void --conf_parse (int trans, char *buf, size_t sz) --{ -- char *cp = buf; -- char *bufend = buf + sz; -- char *line; -- -- line = cp; -- while (cp < bufend) -- { -- if (*cp == '\n') -- { -- /* Check for escaped newlines. */ -- if (cp > buf && *(cp - 1) == '\\') -- *(cp - 1) = *cp = ' '; -- else -- { -- *cp = '\0'; -- conf_parse_line (trans, line, cp - line); -- line = cp + 1; -- } -- } -- cp++; -- } -- if (cp != line) -- warnx("conf_parse: last line non-terminated, ignored."); --} -- --static void --conf_load_defaults (int tr) --{ -- /* No defaults */ -- return; --} -- --void --conf_init (void) --{ -- unsigned int i; -- -- for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++) -- LIST_INIT (&conf_bindings[i]); -- TAILQ_INIT (&conf_trans_queue); -- conf_reinit (); --} -- --/* Open the config file and map it into our address space, then parse it. */ --void --conf_reinit (void) --{ -- struct conf_binding *cb = 0; -- int fd, trans; -- unsigned int i; -- size_t sz; -- char *new_conf_addr = 0; -- struct stat sb; -- -- if ((stat (conf_path, &sb) == 0) || (errno != ENOENT)) -- { -- sz = sb.st_size; -- fd = open (conf_path, O_RDONLY, 0); -- if (fd == -1) -- { -- warnx("conf_reinit: open (\"%s\", O_RDONLY) failed", conf_path); -- return; -- } -- -- new_conf_addr = malloc (sz); -- if (!new_conf_addr) -- { -- warnx("conf_reinit: malloc (%lu) failed", (unsigned long)sz); -- goto fail; -- } -- -- /* XXX I assume short reads won't happen here. */ -- if (read (fd, new_conf_addr, sz) != (int)sz) -- { -- warnx("conf_reinit: read (%d, %p, %lu) failed", -- fd, new_conf_addr, (unsigned long)sz); -- goto fail; -- } -- close (fd); -- -- trans = conf_begin (); -- -- /* XXX Should we not care about errors and rollback? */ -- conf_parse (trans, new_conf_addr, sz); -- } -- else -- trans = conf_begin (); -- -- /* Load default configuration values. */ -- conf_load_defaults (trans); -- -- /* Free potential existing configuration. */ -- if (conf_addr) -- { -- for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++) -- for (cb = LIST_FIRST (&conf_bindings[i]); cb; -- cb = LIST_FIRST (&conf_bindings[i])) -- conf_remove_now (cb->section, cb->tag); -- free (conf_addr); -- } -- -- conf_end (trans, 1); -- conf_addr = new_conf_addr; -- return; -- -- fail: -- if (new_conf_addr) -- free (new_conf_addr); -- close (fd); --} -- --/* -- * Return the numeric value denoted by TAG in section SECTION or DEF -- * if that tag does not exist. -- */ --int --conf_get_num (char *section, char *tag, int def) --{ -- char *value = conf_get_str (section, tag); -- -- if (value) -- return atoi (value); -- return def; --} -- --/* Validate X according to the range denoted by TAG in section SECTION. */ --int --conf_match_num (char *section, char *tag, int x) --{ -- char *value = conf_get_str (section, tag); -- int val, min, max, n; -- -- if (!value) -- return 0; -- n = sscanf (value, "%d,%d:%d", &val, &min, &max); -- switch (n) -- { -- case 1: -- warnx("conf_match_num: %s:%s %d==%d?", section, tag, val, x); -- return x == val; -- case 3: -- warnx("conf_match_num: %s:%s %d<=%d<=%d?", section, tag, min, x, max); -- return min <= x && max >= x; -- default: -- warnx("conf_match_num: section %s tag %s: invalid number spec %s", -- section, tag, value); -- } -- return 0; --} -- --/* Return the string value denoted by TAG in section SECTION. */ --char * --conf_get_str (char *section, char *tag) --{ -- struct conf_binding *cb; -- -- for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb; -- cb = LIST_NEXT (cb, link)) -- if (strcasecmp (section, cb->section) == 0 -- && strcasecmp (tag, cb->tag) == 0) -- { -- return cb->value; -- } -- return 0; --} -- --/* -- * Build a list of string values out of the comma separated value denoted by -- * TAG in SECTION. -- */ --struct conf_list * --conf_get_list (char *section, char *tag) --{ -- char *liststr = 0, *p, *field, *t; -- struct conf_list *list = 0; -- struct conf_list_node *node; -- -- list = malloc (sizeof *list); -- if (!list) -- goto cleanup; -- TAILQ_INIT (&list->fields); -- list->cnt = 0; -- liststr = conf_get_str (section, tag); -- if (!liststr) -- goto cleanup; -- liststr = strdup (liststr); -- if (!liststr) -- goto cleanup; -- p = liststr; -- while ((field = strsep (&p, ",")) != NULL) -- { -- /* Skip leading whitespace */ -- while (isspace (*field)) -- field++; -- /* Skip trailing whitespace */ -- if (p) -- for (t = p - 1; t > field && isspace (*t); t--) -- *t = '\0'; -- if (*field == '\0') -- { -- warnx("conf_get_list: empty field, ignoring..."); -- continue; -- } -- list->cnt++; -- node = calloc (1, sizeof *node); -- if (!node) -- goto cleanup; -- node->field = strdup (field); -- if (!node->field) { -- free(node); -- goto cleanup; -- } -- TAILQ_INSERT_TAIL (&list->fields, node, link); -- } -- free (liststr); -- return list; -- -- cleanup: -- if (list) -- conf_free_list (list); -- if (liststr) -- free (liststr); -- return 0; --} -- --struct conf_list * --conf_get_tag_list (char *section) --{ -- struct conf_list *list = 0; -- struct conf_list_node *node; -- struct conf_binding *cb; -- -- list = malloc (sizeof *list); -- if (!list) -- goto cleanup; -- TAILQ_INIT (&list->fields); -- list->cnt = 0; -- for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb; -- cb = LIST_NEXT (cb, link)) -- if (strcasecmp (section, cb->section) == 0) -- { -- list->cnt++; -- node = calloc (1, sizeof *node); -- if (!node) -- goto cleanup; -- node->field = strdup (cb->tag); -- if (!node->field) { -- free(node); -- goto cleanup; -- } -- TAILQ_INSERT_TAIL (&list->fields, node, link); -- } -- return list; -- -- cleanup: -- if (list) -- conf_free_list (list); -- return 0; --} -- --/* Decode a PEM encoded buffer. */ --int --conf_decode_base64 (u_int8_t *out, u_int32_t *len, u_char *buf) --{ -- u_int32_t c = 0; -- u_int8_t c1, c2, c3, c4; -- -- while (*buf) -- { -- if (*buf > 127 || (c1 = asc2bin[*buf]) == 255) -- return 0; -- buf++; -- -- if (*buf > 127 || (c2 = asc2bin[*buf]) == 255) -- return 0; -- buf++; -- -- if (*buf == '=') -- { -- c3 = c4 = 0; -- c++; -- -- /* Check last four bit */ -- if (c2 & 0xF) -- return 0; -- -- if (strcmp ((char *)buf, "==") == 0) -- buf++; -- else -- return 0; -- } -- else if (*buf > 127 || (c3 = asc2bin[*buf]) == 255) -- return 0; -- else -- { -- if (*++buf == '=') -- { -- c4 = 0; -- c += 2; -- -- /* Check last two bit */ -- if (c3 & 3) -- return 0; -- -- if (strcmp ((char *)buf, "=")) -- return 0; -- -- } -- else if (*buf > 127 || (c4 = asc2bin[*buf]) == 255) -- return 0; -- else -- c += 3; -- } -- -- buf++; -- *out++ = (c1 << 2) | (c2 >> 4); -- *out++ = (c2 << 4) | (c3 >> 2); -- *out++ = (c3 << 6) | c4; -- } -- -- *len = c; -- return 1; -- --} -- --void --conf_free_list (struct conf_list *list) --{ -- struct conf_list_node *node = TAILQ_FIRST (&list->fields); -- -- while (node) -- { -- TAILQ_REMOVE (&list->fields, node, link); -- if (node->field) -- free (node->field); -- free (node); -- node = TAILQ_FIRST (&list->fields); -- } -- free (list); --} -- --int --conf_begin (void) --{ -- static int seq = 0; -- -- return ++seq; --} -- --static struct conf_trans * --conf_trans_node (int transaction, enum conf_op op) --{ -- struct conf_trans *node; -- -- node = calloc (1, sizeof *node); -- if (!node) -- { -- warnx("conf_trans_node: calloc (1, %lu) failed", -- (unsigned long)sizeof *node); -- return 0; -- } -- node->trans = transaction; -- node->op = op; -- TAILQ_INSERT_TAIL (&conf_trans_queue, node, link); -- return node; --} -- --/* Queue a set operation. */ --int --conf_set (int transaction, char *section, char *tag, char *value, int override, -- int is_default) --{ -- struct conf_trans *node; -- -- node = conf_trans_node (transaction, CONF_SET); -- if (!node) -- return 1; -- node->section = strdup (section); -- if (!node->section) -- { -- warnx("conf_set: strdup (\"%s\") failed", section); -- goto fail; -- } -- node->tag = strdup (tag); -- if (!node->tag) -- { -- warnx("conf_set: strdup (\"%s\") failed", tag); -- goto fail; -- } -- node->value = strdup (value); -- if (!node->value) -- { -- warnx("conf_set: strdup (\"%s\") failed", value); -- goto fail; -- } -- node->override = override; -- node->is_default = is_default; -- return 0; -- -- fail: -- if (node->tag) -- free (node->tag); -- if (node->section) -- free (node->section); -- if (node) -- free (node); -- return 1; --} -- --/* Queue a remove operation. */ --int --conf_remove (int transaction, char *section, char *tag) --{ -- struct conf_trans *node; -- -- node = conf_trans_node (transaction, CONF_REMOVE); -- if (!node) -- goto fail; -- node->section = strdup (section); -- if (!node->section) -- { -- warnx("conf_remove: strdup (\"%s\") failed", section); -- goto fail; -- } -- node->tag = strdup (tag); -- if (!node->tag) -- { -- warnx("conf_remove: strdup (\"%s\") failed", tag); -- goto fail; -- } -- return 0; -- -- fail: -- if (node && node->section) -- free (node->section); -- if (node) -- free (node); -- return 1; --} -- --/* Queue a remove section operation. */ --int --conf_remove_section (int transaction, char *section) --{ -- struct conf_trans *node; -- -- node = conf_trans_node (transaction, CONF_REMOVE_SECTION); -- if (!node) -- goto fail; -- node->section = strdup (section); -- if (!node->section) -- { -- warnx("conf_remove_section: strdup (\"%s\") failed", section); -- goto fail; -- } -- return 0; -- -- fail: -- if (node) -- free (node); -- return 1; --} -- --/* Execute all queued operations for this transaction. Cleanup. */ --int --conf_end (int transaction, int commit) --{ -- struct conf_trans *node, *next; -- -- for (node = TAILQ_FIRST (&conf_trans_queue); node; node = next) -- { -- next = TAILQ_NEXT (node, link); -- if (node->trans == transaction) -- { -- if (commit) -- switch (node->op) -- { -- case CONF_SET: -- conf_set_now (node->section, node->tag, node->value, -- node->override, node->is_default); -- break; -- case CONF_REMOVE: -- conf_remove_now (node->section, node->tag); -- break; -- case CONF_REMOVE_SECTION: -- conf_remove_section_now (node->section); -- break; -- default: -- warnx("conf_end: unknown operation: %d", node->op); -- } -- TAILQ_REMOVE (&conf_trans_queue, node, link); -- if (node->section) -- free (node->section); -- if (node->tag) -- free (node->tag); -- if (node->value) -- free (node->value); -- free (node); -- } -- } -- return 0; --} -- --/* -- * Dump running configuration upon SIGUSR1. -- * Configuration is "stored in reverse order", so reverse it again. -- */ --struct dumper { -- char *s, *v; -- struct dumper *next; --}; -- --static void --conf_report_dump (struct dumper *node) --{ -- /* Recursive, cleanup when we're done. */ -- -- if (node->next) -- conf_report_dump (node->next); -- -- if (node->v) -- warnx("%s=\t%s", node->s, node->v); -- else if (node->s) -- { -- warnx("%s", node->s); -- if (strlen (node->s) > 0) -- free (node->s); -- } -- -- free (node); --} -- --void --conf_report (void) --{ -- struct conf_binding *cb, *last = 0; -- unsigned int i, len; -- char *current_section = (char *)0; -- struct dumper *dumper, *dnode; -- -- dumper = dnode = (struct dumper *)calloc (1, sizeof *dumper); -- if (!dumper) -- goto mem_fail; -- -- warnx("conf_report: dumping running configuration"); -- -- for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++) -- for (cb = LIST_FIRST (&conf_bindings[i]); cb; -- cb = LIST_NEXT (cb, link)) -- { -- if (!cb->is_default) -- { -- /* Dump this entry. */ -- if (!current_section || strcmp (cb->section, current_section)) -- { -- if (current_section) -- { -- len = strlen (current_section) + 3; -- dnode->s = malloc (len); -- if (!dnode->s) -- goto mem_fail; -- -- snprintf (dnode->s, len, "[%s]", current_section); -- dnode->next -- = (struct dumper *)calloc (1, sizeof (struct dumper)); -- dnode = dnode->next; -- if (!dnode) -- goto mem_fail; -- -- dnode->s = ""; -- dnode->next -- = (struct dumper *)calloc (1, sizeof (struct dumper)); -- dnode = dnode->next; -- if (!dnode) -- goto mem_fail; -- } -- current_section = cb->section; -- } -- dnode->s = cb->tag; -- dnode->v = cb->value; -- dnode->next = (struct dumper *)calloc (1, sizeof (struct dumper)); -- dnode = dnode->next; -- if (!dnode) -- goto mem_fail; -- last = cb; -- } -- } -- -- if (last) -- { -- len = strlen (last->section) + 3; -- dnode->s = malloc (len); -- if (!dnode->s) -- goto mem_fail; -- snprintf (dnode->s, len, "[%s]", last->section); -- } -- -- conf_report_dump (dumper); -- -- return; -- -- mem_fail: -- warnx("conf_report: malloc/calloc failed"); -- while ((dnode = dumper) != 0) -- { -- dumper = dumper->next; -- if (dnode->s) -- free (dnode->s); -- free (dnode); -- } -- return; --} -diff --git a/utils/idmapd/cfg.h b/utils/idmapd/cfg.h -deleted file mode 100644 -index c1ca940..0000000 ---- a/utils/idmapd/cfg.h -+++ /dev/null -@@ -1,67 +0,0 @@ --/* $OpenBSD: conf.h,v 1.30 2004/06/25 20:25:34 hshoexer Exp $ */ --/* $EOM: conf.h,v 1.13 2000/09/18 00:01:47 ho Exp $ */ -- --/* -- * Copyright (c) 1998, 1999, 2001 Niklas Hallqvist. All rights reserved. -- * Copyright (c) 2000, 2003 H�kan Olsson. All rights reserved. -- * -- * Redistribution and use in source and binary forms, with or without -- * modification, are permitted provided that the following conditions -- * are met: -- * 1. Redistributions of source code must retain the above copyright -- * notice, this list of conditions and the following disclaimer. -- * 2. Redistributions in binary form must reproduce the above copyright -- * notice, this list of conditions and the following disclaimer in the -- * documentation and/or other materials provided with the distribution. -- * -- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- */ -- --/* -- * This code was written under funding by Ericsson Radio Systems. -- */ -- --#ifndef _CONF_H_ --#define _CONF_H_ -- --#include "queue.h" -- --struct conf_list_node { -- TAILQ_ENTRY(conf_list_node) link; -- char *field; --}; -- --struct conf_list { -- size_t cnt; -- TAILQ_HEAD(conf_list_fields_head, conf_list_node) fields; --}; -- --extern char *conf_path; -- --extern int conf_begin(void); --extern int conf_decode_base64(u_int8_t *, u_int32_t *, u_char *); --extern int conf_end(int, int); --extern void conf_free_list(struct conf_list *); --extern struct sockaddr *conf_get_address(char *, char *); --extern struct conf_list *conf_get_list(char *, char *); --extern struct conf_list *conf_get_tag_list(char *); --extern int conf_get_num(char *, char *, int); --extern char *conf_get_str(char *, char *); --extern void conf_init(void); --extern int conf_match_num(char *, char *, int); --extern void conf_reinit(void); --extern int conf_remove(int, char *, char *); --extern int conf_remove_section(int, char *); --extern int conf_set(int, char *, char *, char *, int, int); --extern void conf_report(void); -- --#endif /* _CONF_H_ */ -diff --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c -index 9cbe96c..65a6a2a 100644 ---- a/utils/idmapd/idmapd.c -+++ b/utils/idmapd/idmapd.c -@@ -66,7 +66,7 @@ - #endif /* HAVE_CONFIG_H */ - - #include "xlog.h" --#include "cfg.h" -+#include "conffile.h" - #include "queue.h" - #include "nfslib.h" - -@@ -156,7 +156,7 @@ static char *nobodyuser, *nobodygroup; - static uid_t nobodyuid; - static gid_t nobodygid; - --/* Used by cfg.c */ -+/* Used by conffile.c in libnfs.a */ - char *conf_path; - - static int -diff --git a/utils/mount/Makefile.am b/utils/mount/Makefile.am -index 459fa45..299384a 100644 ---- a/utils/mount/Makefile.am -+++ b/utils/mount/Makefile.am -@@ -15,7 +15,14 @@ mount_nfs_SOURCES = mount.c error.c network.c fstab.c token.c \ - nfsumount.c \ - mount_constants.h error.h network.h fstab.h token.h \ - parse_opt.h parse_dev.h \ -- nfs4_mount.h nfs_mount4.h stropts.h version.h -+ nfs4_mount.h nfs_mount4.h stropts.h version.h \ -+ mount_config.h -+ -+if MOUNT_CONFIG -+mount_nfs_SOURCES += configfile.c -+man5_MANS += nfsmount.conf.man -+EXTRA_DIST += nfsmount.conf -+endif - - mount_nfs_LDADD = ../../support/nfs/libnfs.a \ - ../../support/export/libexport.a -diff --git a/utils/mount/configfile.c b/utils/mount/configfile.c -new file mode 100644 -index 0000000..e347b0e ---- /dev/null -+++ b/utils/mount/configfile.c -@@ -0,0 +1,320 @@ -+/* -+ * configfile.c -- mount configuration file manipulation -+ * Copyright (C) 2008 Red Hat, Inc -+ * -+ * - Routines use to create mount options from the mount -+ * configuration file. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2, or (at your option) -+ * any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ */ -+#ifdef HAVE_CONFIG_H -+#include -+#endif -+#include -+ -+#include -+#include -+#include -+#include -+ -+#include "xlog.h" -+#include "conffile.h" -+ -+#define KBYTES(x) ((x) * (1024)) -+#define MEGABYTES(x) ((x) * (1048576)) -+#define GIGABYTES(x) ((x) * (1073741824)) -+ -+#ifndef NFSMOUNT_GLOBAL_OPTS -+#define NFSMOUNT_GLOBAL_OPTS "NFSMount_Global_Options" -+#endif -+ -+#ifndef NFSMOUNT_MOUNTPOINT -+#define NFSMOUNT_MOUNTPOINT "MountPoint" -+#endif -+ -+#ifndef NFSMOUNT_SERVER -+#define NFSMOUNT_SERVER "Server" -+#endif -+ -+#ifndef MOUNTOPTS_CONFFILE -+#define MOUNTOPTS_CONFFILE "/etc/nfsmount.conf" -+#endif -+char *conf_path = MOUNTOPTS_CONFFILE; -+enum { -+ MNT_NOARG=0, -+ MNT_INTARG, -+ MNT_STRARG, -+ MNT_SPEC, -+ MNT_UNSET -+}; -+struct mnt_alias { -+ char *alias; -+ char *opt; -+ int argtype; -+} mnt_alias_tab[] = { -+ {"background", "bg", MNT_NOARG}, -+ {"foreground", "fg", MNT_NOARG}, -+ {"sloppy", "sloppy", MNT_NOARG}, -+}; -+int mnt_alias_sz = (sizeof(mnt_alias_tab)/sizeof(mnt_alias_tab[0])); -+ -+/* -+ * See if the option is an alias, if so return the -+ * real mount option along with the argument type. -+ */ -+inline static -+char *mountopts_alias(char *opt, int *argtype) -+{ -+ int i; -+ -+ *argtype = MNT_UNSET; -+ for (i=0; i < mnt_alias_sz; i++) { -+ if (strcasecmp(opt, mnt_alias_tab[i].alias) != 0) -+ continue; -+ *argtype = mnt_alias_tab[i].argtype; -+ return mnt_alias_tab[i].opt; -+ } -+ /* Make option names case-insensitive */ -+ upper2lower(opt); -+ -+ return opt; -+} -+/* -+ * Convert numeric strings that end with 'k', 'm' or 'g' -+ * into numeric strings with the real value. -+ * Meaning '8k' becomes '8094'. -+ */ -+char *mountopts_convert(char *value) -+{ -+ unsigned long long factor, num; -+ static char buf[64]; -+ char *ch; -+ -+ ch = &value[strlen(value)-1]; -+ switch (tolower(*ch)) { -+ case 'k': -+ factor = KBYTES(1); -+ break; -+ case 'm': -+ factor = MEGABYTES(1); -+ break; -+ case 'g': -+ factor = GIGABYTES(1); -+ break; -+ default: -+ return value; -+ } -+ *ch = '\0'; -+ if (strncmp(value, "0x", 2) == 0) { -+ num = strtol(value, (char **)NULL, 16); -+ } else if (strncmp(value, "0", 1) == 0) { -+ num = strtol(value, (char **)NULL, 8); -+ } else { -+ num = strtol(value, (char **)NULL, 10); -+ } -+ num *= factor; -+ snprintf(buf, 64, "%lld", num); -+ -+ return buf; -+} -+ -+struct entry { -+ SLIST_ENTRY(entry) entries; -+ char *opt; -+}; -+static SLIST_HEAD(shead, entry) head = SLIST_HEAD_INITIALIZER(head); -+static int list_size; -+ -+/* -+ * Add option to the link list -+ */ -+inline static void -+add_entry(char *opt) -+{ -+ struct entry *entry; -+ -+ entry = calloc(1, sizeof(struct entry)); -+ if (entry == NULL) { -+ xlog_warn("Unable calloc memory for mount configs"); -+ return; -+ } -+ entry->opt = strdup(opt); -+ if (entry->opt == NULL) { -+ xlog_warn("Unable calloc memory for mount opts"); -+ free(entry); -+ return; -+ } -+ SLIST_INSERT_HEAD(&head, entry, entries); -+} -+/* -+ * See if the given entry exists if the link list, -+ * if so return that entry -+ */ -+inline static -+char *lookup_entry(char *opt) -+{ -+ struct entry *entry; -+ -+ SLIST_FOREACH(entry, &head, entries) { -+ if (strcasecmp(entry->opt, opt) == 0) -+ return opt; -+ } -+ return NULL; -+} -+/* -+ * Free all entries on the link list -+ */ -+inline static -+void free_all(void) -+{ -+ struct entry *entry; -+ -+ while (!SLIST_EMPTY(&head)) { -+ entry = SLIST_FIRST(&head); -+ SLIST_REMOVE_HEAD(&head, entries); -+ free(entry->opt); -+ free(entry); -+ } -+} -+/* -+ * Parse the given section of the configuration -+ * file to if there are any mount options set. -+ * If so, added them to link list. -+ */ -+static void -+conf_parse_mntopts(char *section, char *arg, char *opts) -+{ -+ struct conf_list *list; -+ struct conf_list_node *node; -+ char buf[BUFSIZ], *value, *field; -+ char *nvalue, *ptr; -+ int argtype; -+ -+ list = conf_get_tag_list(section); -+ TAILQ_FOREACH(node, &list->fields, link) { -+ /* -+ * Do not overwrite options if already exists -+ */ -+ snprintf(buf, BUFSIZ, "%s=", node->field); -+ if (opts && strcasestr(opts, buf) != NULL) -+ continue; -+ if (lookup_entry(node->field) != NULL) -+ continue; -+ buf[0] = '\0'; -+ value = conf_get_section(section, arg, node->field); -+ if (value == NULL) -+ continue; -+ field = mountopts_alias(node->field, &argtype); -+ if (strcasecmp(value, "false") == 0) { -+ if (argtype != MNT_NOARG) -+ snprintf(buf, BUFSIZ, "no%s", field); -+ } else if (strcasecmp(value, "true") == 0) { -+ snprintf(buf, BUFSIZ, "%s", field); -+ } else { -+ nvalue = strdup(value); -+ ptr = mountopts_convert(nvalue); -+ snprintf(buf, BUFSIZ, "%s=%s", field, ptr); -+ free(nvalue); -+ } -+ if (buf[0] == '\0') -+ continue; -+ /* -+ * Keep a running tally of the list size adding -+ * one for the ',' that will be appened later -+ */ -+ list_size += strlen(buf) + 1; -+ add_entry(buf); -+ } -+ conf_free_list(list); -+} -+ -+/* -+ * Concatenate options from the configuration file with the -+ * given options by building a link list of options from the -+ * different sections in the conf file. Options that exists -+ * in the either the given options or link list are not -+ * overwritten so it matter which when each section is -+ * parsed. -+ */ -+char *conf_get_mntopts(char *spec, char *mount_point, -+ char *mount_opts) -+{ -+ struct entry *entry; -+ char *ptr, *server, *config_opts; -+ int optlen = 0; -+ -+ SLIST_INIT(&head); -+ list_size = 0; -+ /* -+ * First see if there are any mount options relative -+ * to the mount point. -+ */ -+ conf_parse_mntopts(NFSMOUNT_MOUNTPOINT, mount_point, mount_opts); -+ -+ /* -+ * Next, see if there are any mount options relative -+ * to the server -+ */ -+ server = strdup(spec); -+ if (server == NULL) { -+ xlog_warn("conf_get_mountops: Unable calloc memory for server"); -+ free_all(); -+ return mount_opts; -+ } -+ if ((ptr = strchr(server, ':')) != NULL) -+ *ptr='\0'; -+ conf_parse_mntopts(NFSMOUNT_SERVER, server, mount_opts); -+ free(server); -+ -+ /* -+ * Finally process all the global mount options. -+ */ -+ conf_parse_mntopts(NFSMOUNT_GLOBAL_OPTS, NULL, mount_opts); -+ -+ /* -+ * If no mount options were found in the configuration file -+ * just return what was passed in . -+ */ -+ if (SLIST_EMPTY(&head)) -+ return mount_opts; -+ -+ /* -+ * Found options in the configuration file. So -+ * concatenate the configuration options with the -+ * options that were passed in -+ */ -+ if (mount_opts) -+ optlen = strlen(mount_opts); -+ -+ /* list_size + optlen + ',' + '\0' */ -+ config_opts = calloc(1, (list_size+optlen+2)); -+ if (server == NULL) { -+ xlog_warn("conf_get_mountops: Unable calloc memory for config_opts"); -+ free_all(); -+ return mount_opts; -+ } -+ if (mount_opts) { -+ strcpy(config_opts, mount_opts); -+ strcat(config_opts, ","); -+ } -+ SLIST_FOREACH(entry, &head, entries) { -+ strcat(config_opts, entry->opt); -+ strcat(config_opts, ","); -+ } -+ *(strrchr(config_opts, ',')) = '\0'; -+ -+ free_all(); -+ if (mount_opts) -+ free(mount_opts); -+ -+ return config_opts; -+} -diff --git a/utils/mount/mount.c b/utils/mount/mount.c -index a668cd9..355df79 100644 ---- a/utils/mount/mount.c -+++ b/utils/mount/mount.c -@@ -37,6 +37,7 @@ - #include "xcommon.h" - #include "nls.h" - #include "mount_constants.h" -+#include "mount_config.h" - #include "nfs_paths.h" - #include "nfs_mntent.h" - -@@ -474,6 +475,8 @@ int main(int argc, char *argv[]) - spec = argv[1]; - mount_point = argv[2]; - -+ mount_config_init(progname); -+ - argv[2] = argv[0]; /* so that getopt error messages are correct */ - while ((c = getopt_long(argc - 2, argv + 2, "rvVwfno:hs", - longopts, NULL)) != -1) { -@@ -559,6 +562,10 @@ int main(int argc, char *argv[]) - mnt_err = EX_USAGE; - goto out; - } -+ /* -+ * Concatenate mount options from the configuration file -+ */ -+ mount_opts = mount_config_opts(spec, mount_point, mount_opts); - - parse_opts(mount_opts, &flags, &extra_opts); - -diff --git a/utils/mount/mount_config.h b/utils/mount/mount_config.h -new file mode 100644 -index 0000000..9a885a9 ---- /dev/null -+++ b/utils/mount/mount_config.h -@@ -0,0 +1,48 @@ -+#ifndef _LINUX_MOUNT__CONFIG_H -+#define _LINUX_MOUNT_CONFIG__H -+/* -+ * mount_config.h -- mount configuration file routines -+ * Copyright (C) 2008 Red Hat, Inc -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2, or (at your option) -+ * any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ */ -+ -+inline void mount_config_init(char *); -+ -+#ifdef MOUNT_CONFIG -+#include "conffile.h" -+extern char *conf_get_mntopts(char *, char *, char *); -+ -+inline void mount_config_init(char *program) -+{ -+ xlog_open(program); -+ /* -+ * Read the the default mount options -+ */ -+ conf_init(); -+} -+inline char *mount_config_opts(char *spec, -+ char *mount_point, char *mount_opts) -+{ -+ return conf_get_mntopts(spec, mount_point, mount_opts); -+} -+#else /* MOUNT_CONFIG */ -+ -+inline void mount_config_init(char *program) { } -+ -+inline char *mount_config_opts(char *spec, -+ char *mount_point, char *mount_opts) -+{ -+ return mount_opts; -+} -+#endif /* MOUNT_CONFIG */ -+#endif -diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man -index 13de524..2299637 100644 ---- a/utils/mount/nfs.man -+++ b/utils/mount/nfs.man -@@ -28,7 +28,7 @@ The - file describes how - .BR mount (8) - should assemble a system's file name hierarchy --from various independent file systems -+from various independent file systems - (including file systems exported by NFS servers). - Each line in the - .I /etc/fstab -@@ -56,7 +56,7 @@ by NFS, thus conventionally each contain the digit zero. For example: - .P - The server's hostname and export pathname - are separated by a colon, while --the mount options are separated by commas. The remaining fields -+the mount options are separated by commas. The remaining fields - are separated by blanks or tabs. - The server's hostname can be an unqualified hostname, - a fully qualified domain name, -@@ -70,17 +70,16 @@ The - and - .B nfs4 - file system types share similar mount options, --which are described below. -+which are described below. - .SH "MOUNT OPTIONS" --Refer to -+Refer to - .BR mount (8) - for a description of generic mount options --available for all file systems. If you do not need to --specify any mount options, use the generic option -+available for all file systems. If you do not need to -+specify any mount options, use the generic option - .B defaults - in - .IR /etc/fstab . --. - .DT - .SS "Valid options for either the nfs or nfs4 file system type" - These options are valid to use when mounting either -@@ -100,7 +99,7 @@ option is specified), NFS requests are retried indefinitely. - If the - .B soft - option is specified, then the NFS client fails an NFS request --after -+after - .B retrans - retransmissions have been sent, - causing the NFS client to return an error -@@ -119,8 +118,8 @@ option may mitigate some of the risks of using the - option. - .TP 1.5i - .BI timeo= n --The time (in tenths of a second) the NFS client waits for a --response before it retries an NFS request. If this -+The time (in tenths of a second) the NFS client waits for a -+response before it retries an NFS request. If this - option is not specified, requests are retried every - 60 seconds for NFS over TCP. - The NFS client does not perform any kind of timeout backoff -@@ -128,7 +127,7 @@ for NFS over TCP. - .IP - However, for NFS over UDP, the client uses an adaptive - algorithm to estimate an appropriate timeout value for frequently used --request types (such as READ and WRITE requests), but uses the -+request types (such as READ and WRITE requests), but uses the - .B timeo - setting for infrequently used request types (such as FSINFO requests). - If the -@@ -141,13 +140,13 @@ up to a maximum timeout length of 60 seconds. - .TP 1.5i - .BI retrans= n - The number of times the NFS client retries a request before --it attempts further recovery action. If the -+it attempts further recovery action. If the - .B retrans --option is not specified, the NFS client tries each request -+option is not specified, the NFS client tries each request - three times. - .IP - The NFS client generates a "server not responding" message --after -+after - .B retrans - retries, then attempts further recovery (depending on whether the - .B hard -@@ -166,21 +165,21 @@ is 1,048,576 bytes (one megabyte). - The - .B rsize - value is a positive integral multiple of 1024. --Specified -+Specified - .B rsize - values lower than 1024 are replaced with 4096; values larger than - 1048576 are replaced with 1048576. If a specified value is within the supported --range but not a multiple of 1024, it is rounded down to the nearest -+range but not a multiple of 1024, it is rounded down to the nearest - multiple of 1024. - .IP - If an - .B rsize --value is not specified, or if the specified --.B rsize -+value is not specified, or if the specified -+.B rsize - value is larger than the maximum that either client or server can support, - the client and server negotiate the largest - .B rsize --value that they can both support. -+value that they can both support. - .IP - The - .B rsize -@@ -197,7 +196,7 @@ file. - .BI wsize= n - The maximum number of bytes per network WRITE request - that the NFS client can send when writing data to a file --on an NFS server. The actual data payload size of each -+on an NFS server. The actual data payload size of each - NFS WRITE request is equal to - or smaller than the - .B wsize -@@ -207,19 +206,19 @@ is 1,048,576 bytes (one megabyte). - Similar to - .B rsize - , the --.B wsize -+.B wsize - value is a positive integral multiple of 1024. --Specified -+Specified - .B wsize - values lower than 1024 are replaced with 4096; values larger than - 1048576 are replaced with 1048576. If a specified value is within the supported --range but not a multiple of 1024, it is rounded down to the nearest -+range but not a multiple of 1024, it is rounded down to the nearest - multiple of 1024. - .IP - If a - .B wsize --value is not specified, or if the specified --.B wsize -+value is not specified, or if the specified -+.B wsize - value is larger than the maximum that either client or server can support, - the client and server negotiate the largest - .B wsize -@@ -235,31 +234,31 @@ file. However, the effective - .B wsize - value negotiated by the client and server is reported in the - .I /proc/mounts --file. -+file. - .TP 1.5i - .BR ac " / " noac --Selects whether the client may cache file attributes. If neither --option is specified (or if -+Selects whether the client may cache file attributes. If neither -+option is specified (or if - .B ac --is specified), the client caches file --attributes. -+is specified), the client caches file -+attributes. - .IP --To improve performance, NFS clients cache file --attributes. Every few seconds, an NFS client checks the server's version of each --file's attributes for updates. Changes that occur on the server in --those small intervals remain undetected until the client checks the --server again. The -+To improve performance, NFS clients cache file -+attributes. Every few seconds, an NFS client checks the server's version of each -+file's attributes for updates. Changes that occur on the server in -+those small intervals remain undetected until the client checks the -+server again. The - .B noac --option prevents clients from caching file --attributes so that applications can more quickly detect file changes -+option prevents clients from caching file -+attributes so that applications can more quickly detect file changes - on the server. - .IP --In addition to preventing the client from caching file attributes, --the -+In addition to preventing the client from caching file attributes, -+the - .B noac --option forces application writes to become synchronous so --that local changes to a file become visible on the server --immediately. That way, other clients can quickly detect recent -+option forces application writes to become synchronous so -+that local changes to a file become visible on the server -+immediately. That way, other clients can quickly detect recent - writes when they check the file's attributes. - .IP - Using the -@@ -383,20 +382,20 @@ Refer to the SECURITY CONSIDERATIONS section for details. - .TP 1.5i - .BR sharecache " / " nosharecache - Determines how the client's data cache and attribute cache are shared --when mounting the same export more than once concurrently. Using the --same cache reduces memory requirements on the client and presents --identical file contents to applications when the same remote file is -+when mounting the same export more than once concurrently. Using the -+same cache reduces memory requirements on the client and presents -+identical file contents to applications when the same remote file is - accessed via different mount points. - .IP --If neither option is specified, or if the -+If neither option is specified, or if the - .B sharecache --option is --specified, then a single cache is used for all mount points that --access the same export. If the -+option is -+specified, then a single cache is used for all mount points that -+access the same export. If the - .B nosharecache --option is specified, --then that mount point gets a unique cache. Note that when data and --attribute caches are shared, the mount options from the first mount -+option is specified, -+then that mount point gets a unique cache. Note that when data and -+attribute caches are shared, the mount options from the first mount - point take effect for subsequent concurrent mounts of the same export. - .IP - As of kernel 2.6.18, the behavior specified by -@@ -422,6 +421,49 @@ NFS mount points allowed on a client, but NFS servers must be configured - to allow clients to connect via non-privileged source ports. - .IP - Refer to the SECURITY CONSIDERATIONS section for important details. -+.TP 1.5i -+.BI lookupcache= mode -+Specifies how the kernel manages its cache of directory entries -+for a given mount point. -+.I mode -+can be one of -+.BR all , -+.BR none , -+.BR pos , -+or -+.BR positive . -+This option is supported in kernels 2.6.28 and later. -+.IP -+The Linux NFS client caches the result of all NFS LOOKUP requests. -+If the requested directory entry exists on the server, -+the result is referred to as -+.IR positive . -+If the requested directory entry does not exist on the server, -+the result is referred to as -+.IR negative . -+.IP -+If this option is not specified, or if -+.B all -+is specified, the client assumes both types of directory cache entries -+are valid until their parent directory's cached attributes expire. -+.IP -+If -+.BR pos " or " positive -+is specified, the client assumes positive entries are valid -+until their parent directory's cached attributes expire, but -+always revalidates negative entires before an application -+can use them. -+.IP -+If -+.B none -+is specified, -+the client revalidates both types of directory cache entries -+before an application can use them. -+This permits quick detection of files that were created or removed -+by other clients, but can impact application and server performance. -+.IP -+The DATA AND METADATA COHERENCE section contains a -+detailed discussion of these trade-offs. - .SS "Valid options for the nfs file system type" - Use these options, along with the options in the above subsection, - for mounting the -@@ -446,9 +488,9 @@ In addition to controlling how the NFS client transmits requests to - the server, this mount option also controls how the - .BR mount (8) - command communicates with the server's rpcbind and mountd services. --Specifying -+Specifying - .B proto=tcp --forces all traffic from the -+forces all traffic from the - .BR mount (8) - command and the NFS client to use TCP. - Specifying -@@ -574,9 +616,9 @@ It is included for compatibility with other operating systems. - .TP 1.5i - .BR lock " / " nolock - Selects whether to use the NLM sideband protocol to lock files on the server. --If neither option is specified (or if --.B lock --is specified), NLM locking is used for this mount point. -+If neither option is specified (or if -+.B lock -+is specified), NLM locking is used for this mount point. - When using the - .B nolock - option, applications can lock files, -@@ -598,13 +640,13 @@ that do not support the NLM protocol. - .TP 1.5i - .BR intr " / " nointr - Selects whether to allow signals to interrupt file operations --on this mount point. If neither option --is specified (or if -+on this mount point. If neither option -+is specified (or if - .B nointr - is specified), - signals do not interrupt NFS file operations. If --.B intr --is specified, system calls return EINTR if an in-progress NFS operation is interrupted by -+.B intr -+is specified, system calls return EINTR if an in-progress NFS operation is interrupted by - a signal. - .IP - Using the -@@ -622,13 +664,13 @@ compatibility with older kernels. - .TP 1.5i - .BR cto " / " nocto - Selects whether to use close-to-open cache coherence semantics. --If neither option is specified (or if -+If neither option is specified (or if - .B cto - is specified), the client uses close-to-open --cache coherence semantics. If the --.B nocto -+cache coherence semantics. If the -+.B nocto - option is specified, the client uses a non-standard heuristic to determine when --files on the server have changed. -+files on the server have changed. - .IP - Using the - .B nocto -@@ -640,13 +682,13 @@ of this option in more detail. - .BR acl " / " noacl - Selects whether to use the NFSACL sideband protocol on this mount point. - The NFSACL sideband protocol is a proprietary protocol --implemented in Solaris that manages Access Control Lists. NFSACL was never -+implemented in Solaris that manages Access Control Lists. NFSACL was never - made a standard part of the NFS protocol specification. - .IP --If neither -+If neither - .B acl --nor --.B noacl -+nor -+.B noacl - option is specified, - the NFS client negotiates with the server - to see if the NFSACL protocol is supported, -@@ -660,7 +702,7 @@ Selects whether to use NFS version 3 READDIRPLUS requests. - If this option is not specified, the NFS client uses READDIRPLUS requests - on NFS version 3 mounts to read small directories. - Some applications perform better if the client uses only READDIR requests --for all directories. -+for all directories. - .SS "Valid options for the nfs4 file system type" - Use these options, along with the options in the first subsection above, - for mounting the -@@ -676,8 +718,8 @@ can be either - or - .BR tcp . - All NFS version 4 servers are required to support TCP, --so if this mount option is not specified, the NFS version 4 client --uses the TCP transport protocol. -+so if this mount option is not specified, the NFS version 4 client -+uses the TCP transport protocol. - Refer to the TRANSPORT METHODS section for more details. - .TP 1.5i - .BI port= n -@@ -700,12 +742,12 @@ or the server's NFS service is not available on the advertised port. - .TP 1.5i - .BR intr " / " nointr - Selects whether to allow signals to interrupt file operations --on this mount point. If neither option is specified (or if --.B intr --is specified), system calls return EINTR if an in-progress NFS operation --is interrupted by a signal. If -+on this mount point. If neither option is specified (or if -+.B intr -+is specified), system calls return EINTR if an in-progress NFS operation -+is interrupted by a signal. If - .B nointr --is specified, signals do not -+is specified, signals do not - interrupt NFS operations. - .IP - Using the -@@ -727,7 +769,7 @@ for NFS directories on this mount point. - If neither - .B cto - nor --.B nocto -+.B nocto - is specified, - the default is to use close-to-open cache coherence - semantics for directories. -@@ -737,11 +779,11 @@ The DATA AND METADATA COHERENCE section discusses - the behavior of this option in more detail. - .TP 1.5i - .BI clientaddr= n.n.n.n --Specifies a single IPv4 address (in dotted-quad form) --that the NFS client advertises to allow servers --to perform NFS version 4 callback requests against --files on this mount point. If the server is unable to --establish callback connections to clients, performance -+Specifies a single IPv4 address (in dotted-quad form) -+that the NFS client advertises to allow servers -+to perform NFS version 4 callback requests against -+files on this mount point. If the server is unable to -+establish callback connections to clients, performance - may degrade, or accesses to files may temporarily hang. - .IP - If this option is not specified, the -@@ -751,7 +793,14 @@ The automatic discovery process is not perfect, however. - In the presence of multiple client network interfaces, - special routing policies, - or atypical network topologies, --the exact address to use for callbacks may be nontrivial to determine. -+the exact address to use for callbacks may be nontrivial to determine. -+.SH MOUNT CONFIGURATION FILE -+If the mount command is configured to do so, all of the mount options -+described in the previous section can also be configured in the -+.I /etc/nfsmount.conf -+file. See -+.BR nfsmount.conf(5) -+for details. - .SH EXAMPLES - To mount an export using NFS version 2, - use the -@@ -824,33 +873,33 @@ and data transfer size settings for a mount point. - In some cases, however, it pays to specify - these settings explicitly using mount options. - .P --Traditionally, NFS clients used the UDP transport exclusively for --transmitting requests to servers. Though its implementation is --simple, NFS over UDP has many limitations that prevent smooth --operation and good performance in some common deployment --environments. Even an insignificant packet loss rate results in the --loss of whole NFS requests; as such, retransmit timeouts are usually --in the subsecond range to allow clients to recover quickly from --dropped requests, but this can result in extraneous network traffic -+Traditionally, NFS clients used the UDP transport exclusively for -+transmitting requests to servers. Though its implementation is -+simple, NFS over UDP has many limitations that prevent smooth -+operation and good performance in some common deployment -+environments. Even an insignificant packet loss rate results in the -+loss of whole NFS requests; as such, retransmit timeouts are usually -+in the subsecond range to allow clients to recover quickly from -+dropped requests, but this can result in extraneous network traffic - and server load. - .P --However, UDP can be quite effective in specialized settings where --the network’s MTU is large relative to NFS’s data transfer size (such --as network environments that enable jumbo Ethernet frames). In such --environments, trimming the --.B rsize --and --.B wsize --settings so that each --NFS read or write request fits in just a few network frames (or even --in a single frame) is advised. This reduces the probability that --the loss of a single MTU-sized network frame results in the loss of -+However, UDP can be quite effective in specialized settings where -+the networks MTU is large relative to NFSs data transfer size (such -+as network environments that enable jumbo Ethernet frames). In such -+environments, trimming the -+.B rsize -+and -+.B wsize -+settings so that each -+NFS read or write request fits in just a few network frames (or even -+in a single frame) is advised. This reduces the probability that -+the loss of a single MTU-sized network frame results in the loss of - an entire large read or write request. - .P --TCP is the default transport protocol used for all modern NFS -+TCP is the default transport protocol used for all modern NFS - implementations. It performs well in almost every conceivable --network environment and provides excellent guarantees against data --corruption caused by network unreliability. TCP is often a -+network environment and provides excellent guarantees against data -+corruption caused by network unreliability. TCP is often a - requirement for mounting a server through a network firewall. - .P - Under normal circumstances, networks drop packets much more -@@ -861,11 +910,11 @@ After the client exhausts its retransmits (the value of the - .B retrans - mount option), it assumes a network partition has occurred, - and attempts to reconnect to the server on a fresh socket. Since --TCP itself makes network data transfer reliable, -+TCP itself makes network data transfer reliable, - .B rsize --and -+and - .B wsize --can safely be allowed to default to the largest values supported by -+can safely be allowed to default to the largest values supported by - both client and server, independent of the network's MTU size. - .SS "Using the mountproto mount option" - This section applies only to NFS version 2 and version 3 mounts -@@ -950,8 +999,8 @@ Some modern cluster file systems provide - perfect cache coherence among their clients. - Perfect cache coherence among disparate NFS clients - is expensive to achieve, especially on wide area networks. --As such, NFS settles for weaker cache coherence that --satisfies the requirements of most file sharing types. Normally, -+As such, NFS settles for weaker cache coherence that -+satisfies the requirements of most file sharing types. Normally, - file sharing is completely sequential: - first client A opens a file, writes something to it, then closes it; - then client B opens the same file, and reads the changes. -@@ -985,7 +1034,7 @@ it is still difficult to tell whether it was - that client's updates or some other client's updates - that altered the file. - .SS "Attribute caching" --Use the -+Use the - .B noac - mount option to achieve attribute cache coherence - among multiple clients. -@@ -1014,14 +1063,67 @@ The NFS protocol is not designed to support - true cluster file system cache coherence - without some type of application serialization. - If absolute cache coherence among clients is required, --applications should use file locking. Alternatively, applications -+applications should use file locking. Alternatively, applications - can also open their files with the O_DIRECT flag - to disable data caching entirely. -+.SS "Directory entry caching" -+The Linux NFS client caches the result of all NFS LOOKUP requests. -+If the requested directory entry exists on the server, -+the result is referred to as a -+.IR positive " lookup result. -+If the requested directory entry does not exist on the server -+(that is, the server returned ENOENT), -+the result is referred to as -+.IR negative " lookup result. -+.P -+To detect when directory entries have been added or removed -+on the server, -+the Linux NFS client watches a directory's mtime. -+If the client detects a change in a directory's mtime, -+the client drops all cached LOOKUP results for that directory. -+Since the directory's mtime is a cached attribute, it may -+take some time before a client notices it has changed. -+See the descriptions of the -+.BR acdirmin ", " acdirmax ", and " noac -+mount options for more information about -+how long a directory's mtime is cached. -+.P -+Caching directory entries improves the performance of applications that -+do not share files with applications on other clients. -+Using cached information about directories can interfere -+with applications that run concurrently on multiple clients and -+need to detect the creation or removal of files quickly, however. -+The -+.B lookupcache -+mount option allows some tuning of directory entry caching behavior. -+.P -+Before kernel release 2.6.28, -+the Linux NFS client tracked only positive lookup results. -+This permitted applications to detect new directory entries -+created by other clients quickly while still providing some of the -+performance benefits of caching. -+If an application depends on the previous lookup caching behavior -+of the Linux NFS client, you can use -+.BR lookupcache=positive . -+.P -+If the client ignores its cache and validates every application -+lookup request with the server, -+that client can immediately detect when a new directory -+entry has been either created or removed by another client. -+You can specify this behavior using -+.BR lookupcache=none . -+The extra NFS requests needed if the client does not -+cache directory entries can exact a performance penalty. -+Disabling lookup caching -+should result in less of a performance penalty than using -+.BR noac , -+and has no effect on how the NFS client caches the attributes of files. -+.P - .SS "The sync mount option" - The NFS client treats the - .B sync - mount option differently than some other file systems --(refer to -+(refer to - .BR mount (8) - for a description of the generic - .B sync -@@ -1032,16 +1134,16 @@ If neither - .B sync - nor - .B async --is specified (or if the --.B async -+is specified (or if the -+.B async - option is specified), - the NFS client delays sending application - writes to the server --until any of these events occur: -+until any of these events occur: - .IP - Memory pressure forces reclamation of system memory resources. - .IP --An application flushes file data explicitly with -+An application flushes file data explicitly with - .BR sync (2), - .BR msync (2), - or -@@ -1069,7 +1171,7 @@ but at a significant performance cost. - Applications can use the O_SYNC open flag to force application - writes to individual files to go to the server immediately without - the use of the --.B sync -+.B sync - mount option. - .SS "Using file locks with NFS" - The Network Lock Manager protocol is a separate sideband protocol -@@ -1104,15 +1206,15 @@ mount option. NLM locking must be disabled with the - .B nolock - option when using NFS to mount - .I /var --because --.I /var -+because -+.I /var - contains files used by the NLM implementation on Linux. - .P - Specifying the - .B nolock - option may also be advised to improve the performance - of a proprietary application which runs on a single client --and uses file locks extensively. -+and uses file locks extensively. - .SS "NFS version 4 caching features" - The data and metadata caching behavior of NFS version 4 - clients is similar to that of earlier versions. -@@ -1196,7 +1298,7 @@ authentication, and in-transit data protection. - The NFS version 4 specification mandates NFSv4 ACLs, - RPCGSS authentication, and RPCGSS security flavors - that provide per-RPC integrity checking and encryption. --Because NFS version 4 combines the -+Because NFS version 4 combines the - function of the sideband protocols into the main NFS protocol, - the new security features apply to all NFS version 4 operations - including mounting, file locking, and so on. -@@ -1210,11 +1312,11 @@ that is in effect on a given NFS mount point. - Specifying - .B sec=krb5 - provides cryptographic proof of a user's identity in each RPC request. --This provides strong verification of the identity of users -+This provides strong verification of the identity of users - accessing data on the server. - Note that additional configuration besides adding this mount option - is required in order to enable Kerberos security. --Refer to the -+Refer to the - .BR rpc.gssd (8) - man page for details. - .P -@@ -1299,15 +1401,15 @@ filter rules. - It is still possible to mount an NFS server through a firewall, - though some of the - .BR mount (8) --command's automatic service endpoint discovery mechanisms may not work; this -+command's automatic service endpoint discovery mechanisms may not work; this - requires you to provide specific endpoint details via NFS mount options. - .P - NFS servers normally run a portmapper or rpcbind daemon to advertise --their service endpoints to clients. Clients use the rpcbind daemon to determine: -+their service endpoints to clients. Clients use the rpcbind daemon to determine: - .IP - What network port each RPC-based service is using - .IP --What transport protocols each RPC-based service supports -+What transport protocols each RPC-based service supports - .P - The rpcbind daemon uses a well-known port number (111) to help clients find a service endpoint. - Although NFS often uses a standard port number (2049), -@@ -1339,9 +1441,9 @@ of the NFS version 3 specification, however. - .P - The NFS version 4 specification mandates a new version - of Access Control Lists that are semantically richer than POSIX ACLs. --NFS version 4 ACLs are not fully compatible with POSIX ACLs; as such, -+NFS version 4 ACLs are not fully compatible with POSIX ACLs; as such, - some translation between the two is required --in an environment that mixes POSIX ACLs and NFS version 4. -+in an environment that mixes POSIX ACLs and NFS version 4. - .SH FILES - .TP 1.5i - .I /etc/fstab -@@ -1418,4 +1520,4 @@ RFC 1833 for the RPC bind specification. - .br - RFC 2203 for the RPCSEC GSS API protocol specification. - .br --RFC 3530 for the NFS version 4 specification. -+RFC 3530 for the NFS version 4 specification. -diff --git a/utils/mount/nfsmount.conf b/utils/mount/nfsmount.conf -new file mode 100644 -index 0000000..f9fcfcb ---- /dev/null -+++ b/utils/mount/nfsmount.conf -@@ -0,0 +1,120 @@ -+# -+# /etc/nfsmount.conf - see nfsmount.conf(5) for details -+# -+# This is an NFS mount configuration file. This file can be broken -+# up into three different sections: Mount, Server and Global -+# -+# [ MountPoint "Mount_point" ] -+# This section defines all the mount options that -+# should be used on a particular mount point. The '' -+# string need to be an exact match of the path in the mount -+# command. Example: -+# [ MountPoint "/export/home" ] -+# background=True -+# Would cause all mount to /export/home would be done in -+# the background -+# -+# [ Server "Server_Name" ] -+# This section defines all the mount options that -+# should be used on mounts to a particular NFS server. -+# Example: -+# [ Server "nfsserver.foo.com" ] -+# rsize=32k -+# wsize=32k -+# All reads and writes to the 'nfsserver.foo.com' server -+# will be done with 32k (32768 bytes) block sizes. -+# -+#[ NFSMount_Global_Options ] -+# This statically named section defines global mount -+# options that can be applied on all NFS mount. -+# -+# Protocol Version [2,3] -+# Nfsvers=3 -+# Network Transport [Udp,Tcp,Rdma] -+# Proto=Tcp -+# -+# The number of times a request will be retired before -+# generating a timeout -+# Retrans=2 -+# -+# The number of minutes that will retry mount -+# Retry=2 -+# -+# The minimum time (in seconds) file attributes are cached -+# acregmin=30 -+# -+# The Maximum time (in seconds) file attributes are cached -+# acregmin=60 -+# -+# The minimum time (in seconds) directory attributes are cached -+# acregmin=30 -+# -+# The Maximum time (in seconds) directory attributes are cached -+# acregmin=60 -+# -+# Enable Access Control Lists -+# Acl=False -+# -+# Enable Attribute Caching -+# Ac=True -+# -+# Do mounts in background (i.e. asynchronously) -+# Background=False -+# -+# Close-To-Open cache coherence -+# Cto=True -+# -+# Do mounts in foreground (i.e. synchronously) -+# Foreground=True -+# -+# How to handle times out from servers (Hard is STRONGLY suggested) -+# Hard=True -+# Soft=False -+# -+# Enable File Locking -+# Lock=True -+# -+# Enable READDIRPLUS on NFS version 3 mounts -+# Rdirplus=True -+# -+# Maximum Read Size (in Bytes) -+# Rsize=8k -+# -+# Maximum Write Size (in Bytes) -+# Wsize=8k -+# -+# Maximum Server Block Size (in Bytes) -+# Bsize=8k -+# -+# Ignore unknown mount options -+# Sloppy=False -+# -+# Share Data and Attribute Caches -+# Sharecache=True -+# -+# The amount of time, in tenths of a seconds, the client -+# will wait for a response from the server before retransmitting -+# the request. -+# Timeo=600 -+# -+# Sets all attributes times to the same time (in seconds) -+# actimeo=30 -+# -+# Server Mountd port mountport -+# mountport=4001 -+# -+# Server Mountd Protocol -+# mountproto=tcp -+# -+# Server Mountd Version -+# mounvers=3 -+# -+# Server Mountd Host -+# mounthost=hostname -+# -+# Server Port -+# Port=2049 -+# -+# RPCGSS security flavors -+# [none, sys, krb5, krb5i, krb5p ] -+# Sec=sys -diff --git a/utils/mount/nfsmount.conf.man b/utils/mount/nfsmount.conf.man -new file mode 100644 -index 0000000..12a3fe7 ---- /dev/null -+++ b/utils/mount/nfsmount.conf.man -@@ -0,0 +1,87 @@ -+.\"@(#)nfsmount.conf.5" -+.TH NFSMOUNT.CONF 5 "9 Mar 2008" -+.SH NAME -+nfsmount.conf - Configuration file for NFS mounts -+.SH SYNOPSIS -+Configuration file for NFS mounts that allows options -+to be set globally, per server or per mount point. -+.SH DESCRIPTION -+The configuration file is made up of multiple sections -+followed by variables associated with that section. -+A section is defined by a string enclosed by -+.BR [ -+and -+.BR ] -+branches. -+Variables are assignment statements that assign values -+to particular variables using the -+.BR = -+operator, as in -+.BR Proto=Tcp . -+Sections are broken up into three basic categories: -+Global options, Server options and Mount Point options. -+.HP -+.B [ NFSMount_Global_Options ] -+- This statically named section -+defines all of the global mount options that can be -+applied to every NFS mount. -+.HP -+.B [ Server \(lqServer_Name\(rq ] -+- This section defines all the mount options that should -+be used on mounts to a particular NFS server. The -+.I \(lqServer_Name\(rq -+strings needs to be surrounded by '\(lq' and -+be an exact match of the server name used in the -+.B mount -+command. -+.HP -+.B [ MountPoint \(lqMount_Point\(rq ] -+- This section defines all the mount options that -+should be used on a particular mount point. -+The -+.I \(lqMount_Point\(rq -+string needs to be surrounded by '\(lq' and be an -+exact match of the mount point used in the -+.BR mount -+command. -+.SH EXAMPLES -+.PP -+These are some example lines of how sections and variables -+are defined in the configuration file. -+.PP -+[ NFSMount_Global_Options ] -+.br -+ Proto=Tcp -+.RS -+.HP -+The TCP protocol will be used on every NFS mount. -+.HP -+.RE -+[ Server \(lqnfsserver.foo.com\(rq ] -+.br -+ rsize=32k -+.br -+ wsize=32k -+.HP -+.RS -+A 33k (32768 bytes) block size will be used as the read and write -+size on all mounts to the 'nfsserver.foo.com' server. -+.HP -+.RE -+.BR -+[ MountPoint \(lq/export/home\(rq ] -+.br -+ Background=True -+.RS -+.HP -+All mounts to the '/export/home' export will be performed in -+the background (i.e. done asynchronously). -+.HP -+.SH FILES -+.TP 10n -+.I /etc/nfsmount.conf -+Default NFS mount configuration file -+.PD -+.SH SEE ALSO -+.BR nfs (5), -+.BR mount (8), -diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c -index 9bbbfb3..e4e2f22 100644 ---- a/utils/mountd/cache.c -+++ b/utils/mountd/cache.c -@@ -564,7 +564,7 @@ static void write_fsloc(FILE *f, struct exportent *ep, char *path) - release_replicas(servers); - } - --static void write_secinfo(FILE *f, struct exportent *ep) -+static void write_secinfo(FILE *f, struct exportent *ep, int flag_mask) - { - struct sec_entry *p; - -@@ -578,7 +578,7 @@ static void write_secinfo(FILE *f, struct exportent *ep) - qword_printint(f, p - ep->e_secinfo); - for (p = ep->e_secinfo; p->flav; p++) { - qword_printint(f, p->flav->fnum); -- qword_printint(f, p->flags); -+ qword_printint(f, p->flags & flag_mask); - } - - } -@@ -590,16 +590,14 @@ static int dump_to_cache(FILE *f, char *domain, char *path, struct exportent *ex - qword_printint(f, time(0)+30*60); - if (exp) { - int different_fs = strcmp(path, exp->e_path) != 0; -- -- if (different_fs) -- qword_printint(f, exp->e_flags & ~NFSEXP_FSID); -- else -- qword_printint(f, exp->e_flags); -+ int flag_mask = different_fs ? ~NFSEXP_FSID : ~0; -+ -+ qword_printint(f, exp->e_flags & flag_mask); - qword_printint(f, exp->e_anonuid); - qword_printint(f, exp->e_anongid); - qword_printint(f, exp->e_fsid); - write_fsloc(f, exp, path); -- write_secinfo(f, exp); -+ write_secinfo(f, exp, flag_mask); - if (exp->e_uuid == NULL || different_fs) { - char u[16]; - if (get_uuid(path, NULL, 16, u)) { -diff --git a/utils/nfsstat/nfsstat.c b/utils/nfsstat/nfsstat.c -index 7e9f327..fa46d5d 100644 ---- a/utils/nfsstat/nfsstat.c -+++ b/utils/nfsstat/nfsstat.c -@@ -30,8 +30,8 @@ static unsigned int cltproc2info[20], cltproc2info_old[20]; /* NFSv2 call counts - static unsigned int srvproc3info[24], srvproc3info_old[24]; /* NFSv3 call counts ([0] == 22) */ - static unsigned int cltproc3info[24], cltproc3info_old[24]; /* NFSv3 call counts ([0] == 22) */ - static unsigned int srvproc4info[4], srvproc4info_old[4]; /* NFSv4 call counts ([0] == 2) */ --static unsigned int cltproc4info[37], cltproc4info_old[37]; /* NFSv4 call counts ([0] == 35) */ --static unsigned int srvproc4opsinfo[42], srvproc4opsinfo_old[42]; /* NFSv4 call counts ([0] == 40) */ -+static unsigned int cltproc4info[49], cltproc4info_old[49]; /* NFSv4 call counts ([0] == 35) */ -+static unsigned int srvproc4opsinfo[61], srvproc4opsinfo_old[61]; /* NFSv4 call counts ([0] == 40) */ - static unsigned int srvnetinfo[5], srvnetinfo_old[5]; /* 0 # of received packets - * 1 UDP packets - * 2 TCP packets -@@ -93,24 +93,58 @@ static const char * nfssrvproc4name[2] = { - "compound", - }; - --static const char * nfscltproc4name[35] = { -+static const char * nfscltproc4name[47] = { - "null", "read", "write", "commit", "open", "open_conf", - "open_noat", "open_dgrd", "close", "setattr", "fsinfo", "renew", - "setclntid", "confirm", "lock", - "lockt", "locku", "access", "getattr", "lookup", "lookup_root", - "remove", "rename", "link", "symlink", "create", "pathconf", - "statfs", "readlink", "readdir", "server_caps", "delegreturn", "getacl", -- "setacl", "fs_locations" -+ "setacl", "fs_locations", -+ /* nfsv4.1 client ops */ -+ "exchange_id", -+ "create_ses", -+ "destroy_ses", -+ "sequence", -+ "get_lease_t", -+ "layoutget", -+ "layoutcommit", -+ "layoutreturn", -+ "getdevlist", -+ "getdevinfo", -+ /* nfsv4.1 pnfs client ops to data server only */ -+ "ds_write", -+ "ds_commit", - }; - --static const char * nfssrvproc4opname[40] = { -+static const char * nfssrvproc4opname[59] = { - "op0-unused", "op1-unused", "op2-future", "access", "close", "commit", - "create", "delegpurge", "delegreturn", "getattr", "getfh", "link", - "lock", "lockt", "locku", "lookup", "lookup_root", "nverify", - "open", "openattr", "open_conf", "open_dgrd", "putfh", "putpubfh", - "putrootfh", "read", "readdir", "readlink", "remove", "rename", - "renew", "restorefh", "savefh", "secinfo", "setattr", "setcltid", -- "setcltidconf", "verify", "write", "rellockowner" -+ "setcltidconf", "verify", "write", "rellockowner", -+ /* nfsv4.1 server ops */ -+ "bc_ctl", -+ "bind_conn", -+ "exchange_id", -+ "create_ses", -+ "destroy_ses", -+ "free_stateid", -+ "getdirdeleg", -+ "getdevinfo", -+ "getdevlist", -+ "layoutcommit", -+ "layoutget", -+ "layoutreturn", -+ "secinfononam", -+ "sequence", -+ "set_ssv", -+ "test_stateid", -+ "want_deleg", -+ "destroy_clid", -+ "reclaim_comp", - }; - - #define LABEL_srvnet "Server packet stats:\n" diff --git a/nfs-utils-1.2.1-rc5.patch b/nfs-utils-1.2.1-rc5.patch deleted file mode 100644 index 0031a98..0000000 --- a/nfs-utils-1.2.1-rc5.patch +++ /dev/null @@ -1,744 +0,0 @@ -diff --git a/support/include/conffile.h b/support/include/conffile.h -index 132a149..672020a 100644 ---- a/support/include/conffile.h -+++ b/support/include/conffile.h -@@ -34,6 +34,7 @@ - #define _CONFFILE_H_ - - #include -+#include - - struct conf_list_node { - TAILQ_ENTRY(conf_list_node) link; -diff --git a/tools/nfs-iostat/nfs-iostat.py b/tools/nfs-iostat/nfs-iostat.py -index 9626d42..2d0b143 100644 ---- a/tools/nfs-iostat/nfs-iostat.py -+++ b/tools/nfs-iostat/nfs-iostat.py -@@ -1,4 +1,4 @@ --#!/usr/bin/env python -+#!/usr/bin/python - # -*- python-mode -*- - """Emulate iostat for NFS mount points using /proc/self/mountstats - """ -@@ -21,6 +21,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - """ - - import sys, os, time -+from optparse import OptionParser, OptionGroup - - Iostats_version = '0.2' - -@@ -353,6 +354,12 @@ class DeviceData: - print '\t%7.3f' % rtt_per_op, - print '\t%7.3f' % exe_per_op - -+ def ops(self, sample_time): -+ sends = float(self.__rpc_data['rpcsends']) -+ if sample_time == 0: -+ sample_time = float(self.__nfs_data['age']) -+ return (sends / sample_time) -+ - def display_iostats(self, sample_time, which): - """Display NFS and RPC stats in an iostat-like way - """ -@@ -395,33 +402,6 @@ class DeviceData: - # Functions - # - --def print_iostat_help(name): -- print 'usage: %s [ [ ] ] [ ] [ ] ' % name -- print -- print ' Version %s' % Iostats_version -- print -- print ' Sample iostat-like program to display NFS client per-mount statistics.' -- print -- print ' The parameter specifies the amount of time in seconds between' -- print ' each report. The first report contains statistics for the time since each' -- print ' file system was mounted. Each subsequent report contains statistics' -- print ' collected during the interval since the previous report.' -- print -- print ' If the parameter is specified, the value of determines the' -- print ' number of reports generated at seconds apart. If the interval' -- print ' parameter is specified without the parameter, the command generates' -- print ' reports continuously.' -- print -- print ' Options include "--attr", which displays statistics related to the attribute' -- print ' cache, "--dir", which displays statistics related to directory operations,' -- print ' and "--page", which displays statistics related to the page cache.' -- print ' By default, if no option is specified, statistics related to file I/O are' -- print ' displayed.' -- print -- print ' If one or more names are specified, statistics for only these' -- print ' mount points will be displayed. Otherwise, all NFS mount points on the' -- print ' client are listed.' -- - def parse_stats_file(filename): - """pop the contents of a mountstats file into a dictionary, - keyed by mount point. each value object is a list of the -@@ -446,109 +426,198 @@ def parse_stats_file(filename): - - return ms_dict - --def print_iostat_summary(old, new, devices, time, ac): -- for device in devices: -- stats = DeviceData() -- stats.parse_stats(new[device]) -- if not old: -- stats.display_iostats(time, ac) -- else: -+def print_iostat_summary(old, new, devices, time, options): -+ stats = {} -+ diff_stats = {} -+ -+ if old: -+ # Trim device list to only include intersection of old and new data, -+ # this addresses umounts due to autofs mountpoints -+ devicelist = filter(lambda x:x in devices,old) -+ else: -+ devicelist = devices -+ -+ for device in devicelist: -+ stats[device] = DeviceData() -+ stats[device].parse_stats(new[device]) -+ if old: - old_stats = DeviceData() - old_stats.parse_stats(old[device]) -- diff_stats = stats.compare_iostats(old_stats) -- diff_stats.display_iostats(time, ac) -+ diff_stats[device] = stats[device].compare_iostats(old_stats) -+ -+ if options.sort: -+ if old: -+ # We now have compared data and can print a comparison -+ # ordered by mountpoint ops per second -+ devicelist.sort(key=lambda x: diff_stats[x].ops(time), reverse=True) -+ else: -+ # First iteration, just sort by newly parsed ops/s -+ devicelist.sort(key=lambda x: stats[x].ops(time), reverse=True) -+ -+ count = 1 -+ for device in devicelist: -+ if old: -+ diff_stats[device].display_iostats(time, options.which) -+ else: -+ stats[device].display_iostats(time, options.which) -+ -+ count += 1 -+ if (count > options.list): -+ return -+ -+ -+def list_nfs_mounts(givenlist, mountstats): -+ """return a list of NFS mounts given a list to validate or -+ return a full list if the given list is empty - -+ may return an empty list if none found -+ """ -+ list = [] -+ if len(givenlist) > 0: -+ for device in givenlist: -+ stats = DeviceData() -+ stats.parse_stats(mountstats[device]) -+ if stats.is_nfs_mountpoint(): -+ list += [device] -+ else: -+ for device, descr in mountstats.iteritems(): -+ stats = DeviceData() -+ stats.parse_stats(descr) -+ if stats.is_nfs_mountpoint(): -+ list += [device] -+ return list - - def iostat_command(name): - """iostat-like command for NFS mount points - """ - mountstats = parse_stats_file('/proc/self/mountstats') - devices = [] -- which = 0 -+ origdevices = [] - interval_seen = False - count_seen = False - -- for arg in sys.argv: -- if arg in ['-h', '--help', 'help', 'usage']: -- print_iostat_help(name) -- return -- -- if arg in ['-v', '--version', 'version']: -- print '%s version %s' % (name, Iostats_version) -- return -- -- if arg in ['-a', '--attr']: -- which = 1 -- continue -- -- if arg in ['-d', '--dir']: -- which = 2 -- continue -- -- if arg in ['-p', '--page']: -- which = 3 -- continue -+ mydescription= """ -+Sample iostat-like program to display NFS client per-mount' -+statistics. The parameter specifies the amount of time in seconds -+between each report. The first report contains statistics for the time since -+each file system was mounted. Each subsequent report contains statistics -+collected during the interval since the previous report. If the -+parameter is specified, the value of determines the number of reports -+generated at seconds apart. If the interval parameter is specified -+without the parameter, the command generates reports continuously. -+If one or more names are specified, statistics for only these -+mount points will be displayed. Otherwise, all NFS mount points on the -+client are listed. -+""" -+ parser = OptionParser( -+ usage="usage: %prog [ [ ] ] [ ] [ ]", -+ description=mydescription, -+ version='version %s' % Iostats_version) -+ parser.set_defaults(which=0, sort=False, list=sys.maxint) -+ -+ statgroup = OptionGroup(parser, "Statistics Options", -+ 'File I/O is displayed unless one of the following is specified:') -+ statgroup.add_option('-a', '--attr', -+ action="store_const", -+ dest="which", -+ const=1, -+ help='displays statistics related to the attribute cache') -+ statgroup.add_option('-d', '--dir', -+ action="store_const", -+ dest="which", -+ const=2, -+ help='displays statistics related to directory operations') -+ statgroup.add_option('-p', '--page', -+ action="store_const", -+ dest="which", -+ const=3, -+ help='displays statistics related to the page cache') -+ parser.add_option_group(statgroup) -+ displaygroup = OptionGroup(parser, "Display Options", -+ 'Options affecting display format:') -+ displaygroup.add_option('-s', '--sort', -+ action="store_true", -+ dest="sort", -+ help="Sort NFS mount points by ops/second") -+ displaygroup.add_option('-l','--list', -+ action="store", -+ type="int", -+ dest="list", -+ help="only print stats for first LIST mount points") -+ parser.add_option_group(displaygroup) -+ -+ (options, args) = parser.parse_args(sys.argv) -+ -+ for arg in args: - - if arg == sys.argv[0]: - continue - - if arg in mountstats: -- devices += [arg] -+ origdevices += [arg] - elif not interval_seen: -- interval = int(arg) -+ try: -+ interval = int(arg) -+ except: -+ print 'Illegal value %s' % arg -+ return - if interval > 0: - interval_seen = True - else: -- print 'Illegal value' -+ print 'Illegal value %s' % arg - return - elif not count_seen: -- count = int(arg) -+ try: -+ count = int(arg) -+ except: -+ print 'Ilegal value %s' % arg -+ return - if count > 0: - count_seen = True - else: -- print 'Illegal value' -+ print 'Illegal value %s' % arg - return - - # make certain devices contains only NFS mount points -- if len(devices) > 0: -- check = [] -- for device in devices: -- stats = DeviceData() -- stats.parse_stats(mountstats[device]) -- if stats.is_nfs_mountpoint(): -- check += [device] -- devices = check -- else: -- for device, descr in mountstats.iteritems(): -- stats = DeviceData() -- stats.parse_stats(descr) -- if stats.is_nfs_mountpoint(): -- devices += [device] -+ devices = list_nfs_mounts(origdevices, mountstats) - if len(devices) == 0: - print 'No NFS mount points were found' - return - -+ - old_mountstats = None - sample_time = 0.0 - - if not interval_seen: -- print_iostat_summary(old_mountstats, mountstats, devices, sample_time, which) -+ print_iostat_summary(old_mountstats, mountstats, devices, sample_time, options) - return - - if count_seen: - while count != 0: -- print_iostat_summary(old_mountstats, mountstats, devices, sample_time, which) -+ print_iostat_summary(old_mountstats, mountstats, devices, sample_time, options) - old_mountstats = mountstats - time.sleep(interval) - sample_time = interval - mountstats = parse_stats_file('/proc/self/mountstats') -+ # automount mountpoints add and drop, if automount is involved -+ # we need to recheck the devices list when reparsing -+ devices = list_nfs_mounts(origdevices,mountstats) -+ if len(devices) == 0: -+ print 'No NFS mount points were found' -+ return - count -= 1 - else: - while True: -- print_iostat_summary(old_mountstats, mountstats, devices, sample_time, which) -+ print_iostat_summary(old_mountstats, mountstats, devices, sample_time, options) - old_mountstats = mountstats - time.sleep(interval) - sample_time = interval - mountstats = parse_stats_file('/proc/self/mountstats') -+ # automount mountpoints add and drop, if automount is involved -+ # we need to recheck the devices list when reparsing -+ devices = list_nfs_mounts(origdevices,mountstats) -+ if len(devices) == 0: -+ print 'No NFS mount points were found' -+ return - - # - # Main -diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c -index 02239d2..37e2aa5 100644 ---- a/utils/gssd/gssd_proc.c -+++ b/utils/gssd/gssd_proc.c -@@ -840,42 +840,48 @@ handle_krb5_upcall(struct clnt_info *clp) - } - if (create_resp != 0) { - if (uid == 0 && root_uses_machine_creds == 1) { -+ int nocache = 0; - int success = 0; -- -- gssd_refresh_krb5_machine_credential(clp->servername, -- NULL); -- /* -- * Get a list of credential cache names and try each -- * of them until one works or we've tried them all -- */ -- if (gssd_get_krb5_machine_cred_list(&credlist)) { -- printerr(0, "ERROR: No credentials found " -- "for connection to server %s\n", -- clp->servername); -- goto out_return_error; -- } -- for (ccname = credlist; ccname && *ccname; ccname++) { -- gssd_setup_krb5_machine_gss_ccache(*ccname); -- if ((create_auth_rpc_client(clp, &rpc_clnt, -- &auth, uid, -- AUTHTYPE_KRB5)) == 0) { -- /* Success! */ -- success++; -- break; -+ do { -+ gssd_refresh_krb5_machine_credential(clp->servername, -+ NULL, nocache); -+ /* -+ * Get a list of credential cache names and try each -+ * of them until one works or we've tried them all -+ */ -+ if (gssd_get_krb5_machine_cred_list(&credlist)) { -+ printerr(0, "ERROR: No credentials found " -+ "for connection to server %s\n", -+ clp->servername); -+ goto out_return_error; - } -- printerr(2, "WARNING: Failed to create krb5 context " -- "for user with uid %d with credentials " -- "cache %s for server %s\n", -- uid, *ccname, clp->servername); -- } -- gssd_free_krb5_machine_cred_list(credlist); -- if (!success) { -- printerr(1, "WARNING: Failed to create krb5 context " -- "for user with uid %d with any " -- "credentials cache for server %s\n", -- uid, clp->servername); -- goto out_return_error; -- } -+ for (ccname = credlist; ccname && *ccname; ccname++) { -+ gssd_setup_krb5_machine_gss_ccache(*ccname); -+ if ((create_auth_rpc_client(clp, &rpc_clnt, -+ &auth, uid, -+ AUTHTYPE_KRB5)) == 0) { -+ /* Success! */ -+ success++; -+ break; -+ } -+ printerr(2, "WARNING: Failed to create machine krb5 context " -+ "with credentials cache %s for server %s\n", -+ *ccname, clp->servername); -+ } -+ gssd_free_krb5_machine_cred_list(credlist); -+ if (!success) { -+ if(nocache == 0) { -+ nocache++; -+ printerr(2, "WARNING: Machine cache is prematurely expired or corrupted " -+ "trying to recreate cache for server %s\n", clp->servername); -+ } else { -+ printerr(1, "WARNING: Failed to create machine krb5 context " -+ "with any credentials cache for server %s\n", -+ clp->servername); -+ goto out_return_error; -+ } -+ } -+ } while(!success); - } else { - printerr(1, "WARNING: Failed to create krb5 context " - "for user with uid %d for server %s\n", -diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c -index 3009cc5..78e9775 100644 ---- a/utils/gssd/krb5_util.c -+++ b/utils/gssd/krb5_util.c -@@ -137,7 +137,7 @@ static int select_krb5_ccache(const struct dirent *d); - static int gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, - struct dirent **d); - static int gssd_get_single_krb5_cred(krb5_context context, -- krb5_keytab kt, struct gssd_k5_kt_princ *ple); -+ krb5_keytab kt, struct gssd_k5_kt_princ *ple, int nocache); - static int query_krb5_ccache(const char* cred_cache, char **ret_princname, - char **ret_realm); - -@@ -359,7 +359,8 @@ limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid) - static int - gssd_get_single_krb5_cred(krb5_context context, - krb5_keytab kt, -- struct gssd_k5_kt_princ *ple) -+ struct gssd_k5_kt_princ *ple, -+ int nocache) - { - #if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS - krb5_get_init_creds_opt *init_opts = NULL; -@@ -379,7 +380,7 @@ gssd_get_single_krb5_cred(krb5_context context, - - memset(&my_creds, 0, sizeof(my_creds)); - -- if (ple->ccname && ple->endtime > now) { -+ if (ple->ccname && ple->endtime > now && !nocache) { - printerr(2, "INFO: Credentials in CC '%s' are good until %d\n", - ple->ccname, ple->endtime); - code = 0; -@@ -1095,7 +1096,7 @@ gssd_get_krb5_machine_cred_list(char ***list) - for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) { - if (ple->ccname) { - /* Make sure cred is up-to-date before returning it */ -- retval = gssd_refresh_krb5_machine_credential(NULL, ple); -+ retval = gssd_refresh_krb5_machine_credential(NULL, ple, 0); - if (retval) - continue; - if (i + 1 > listsize) { -@@ -1185,7 +1186,7 @@ gssd_destroy_krb5_machine_creds(void) - */ - int - gssd_refresh_krb5_machine_credential(char *hostname, -- struct gssd_k5_kt_princ *ple) -+ struct gssd_k5_kt_princ *ple, int nocache) - { - krb5_error_code code = 0; - krb5_context context; -@@ -1240,7 +1241,7 @@ gssd_refresh_krb5_machine_credential(char *hostname, - goto out; - } - } -- retval = gssd_get_single_krb5_cred(context, kt, ple); -+ retval = gssd_get_single_krb5_cred(context, kt, ple, nocache); - out: - if (kt) - krb5_kt_close(context, kt); -diff --git a/utils/gssd/krb5_util.h b/utils/gssd/krb5_util.h -index 3d39300..4b6b281 100644 ---- a/utils/gssd/krb5_util.h -+++ b/utils/gssd/krb5_util.h -@@ -30,7 +30,7 @@ void gssd_free_krb5_machine_cred_list(char **list); - void gssd_setup_krb5_machine_gss_ccache(char *servername); - void gssd_destroy_krb5_machine_creds(void); - int gssd_refresh_krb5_machine_credential(char *hostname, -- struct gssd_k5_kt_princ *ple); -+ struct gssd_k5_kt_princ *ple, int nocache); - char *gssd_k5_err_msg(krb5_context context, krb5_error_code code); - void gssd_k5_get_default_realm(char **def_realm); - -diff --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c -index 65a6a2a..573abaa 100644 ---- a/utils/idmapd/idmapd.c -+++ b/utils/idmapd/idmapd.c -@@ -139,6 +139,7 @@ static void nametoidres(struct idmap_msg *); - - static int nfsdopen(void); - static int nfsdopenone(struct idmap_client *); -+static void nfsdreopen_one(struct idmap_client *); - static void nfsdreopen(void); - - size_t strlcat(char *, const char *, size_t); -@@ -502,7 +503,8 @@ nfsdcb(int fd, short which, void *data) - xlog_warn("nfsdcb: read(%s) failed: errno %d (%s)", - ic->ic_path, len?errno:0, - len?strerror(errno):"End of File"); -- goto out; -+ nfsdreopen_one(ic); -+ return; - } - - /* Get rid of newline and terminate buffer*/ -@@ -514,11 +516,11 @@ nfsdcb(int fd, short which, void *data) - /* Authentication name -- ignored for now*/ - if (getfield(&bp, authbuf, sizeof(authbuf)) == -1) { - xlog_warn("nfsdcb: bad authentication name in upcall\n"); -- return; -+ goto out; - } - if (getfield(&bp, typebuf, sizeof(typebuf)) == -1) { - xlog_warn("nfsdcb: bad type in upcall\n"); -- return; -+ goto out; - } - if (verbose > 0) - xlog_warn("nfsdcb: authbuf=%s authtype=%s", -@@ -532,26 +534,26 @@ nfsdcb(int fd, short which, void *data) - im.im_conv = IDMAP_CONV_NAMETOID; - if (getfield(&bp, im.im_name, sizeof(im.im_name)) == -1) { - xlog_warn("nfsdcb: bad name in upcall\n"); -- return; -+ goto out; - } - break; - case IC_IDNAME: - im.im_conv = IDMAP_CONV_IDTONAME; - if (getfield(&bp, buf1, sizeof(buf1)) == -1) { - xlog_warn("nfsdcb: bad id in upcall\n"); -- return; -+ goto out; - } - tmp = strtoul(buf1, (char **)NULL, 10); - im.im_id = (u_int32_t)tmp; - if ((tmp == ULONG_MAX && errno == ERANGE) - || (unsigned long)im.im_id != tmp) { - xlog_warn("nfsdcb: id '%s' too big!\n", buf1); -- return; -+ goto out; - } - break; - default: - xlog_warn("nfsdcb: Unknown which type %d", ic->ic_which); -- return; -+ goto out; - } - - imconv(ic, &im); -@@ -612,7 +614,7 @@ nfsdcb(int fd, short which, void *data) - break; - default: - xlog_warn("nfsdcb: Unknown which type %d", ic->ic_which); -- return; -+ goto out; - } - - bsiz = sizeof(buf) - bsiz; -diff --git a/utils/mount/mount_config.h b/utils/mount/mount_config.h -index 9a885a9..3023306 100644 ---- a/utils/mount/mount_config.h -+++ b/utils/mount/mount_config.h -@@ -20,6 +20,8 @@ inline void mount_config_init(char *); - - #ifdef MOUNT_CONFIG - #include "conffile.h" -+#include "xlog.h" -+ - extern char *conf_get_mntopts(char *, char *, char *); - - inline void mount_config_init(char *program) -diff --git a/utils/mount/network.c b/utils/mount/network.c -index f6fa5fd..bd621be 100644 ---- a/utils/mount/network.c -+++ b/utils/mount/network.c -@@ -90,6 +90,7 @@ static const char *nfs_transport_opttbl[] = { - static const char *nfs_version_opttbl[] = { - "v2", - "v3", -+ "v4", - "vers", - "nfsvers", - NULL, -@@ -1203,7 +1204,7 @@ nfs_nfs_program(struct mount_options *options, unsigned long *program) - * Returns TRUE if @version contains a valid value for this option, - * or FALSE if the option was specified with an invalid value. - */ --static int -+int - nfs_nfs_version(struct mount_options *options, unsigned long *version) - { - long tmp; -@@ -1215,10 +1216,13 @@ nfs_nfs_version(struct mount_options *options, unsigned long *version) - case 1: /* v3 */ - *version = 3; - return 1; -- case 2: /* vers */ -+ case 2: /* v4 */ -+ *version = 4; -+ return 1; -+ case 3: /* vers */ - switch (po_get_numeric(options, "vers", &tmp)) { - case PO_FOUND: -- if (tmp >= 2 && tmp <= 3) { -+ if (tmp >= 2 && tmp <= 4) { - *version = tmp; - return 1; - } -@@ -1229,10 +1233,10 @@ nfs_nfs_version(struct mount_options *options, unsigned long *version) - case PO_BAD_VALUE: - return 0; - } -- case 3: /* nfsvers */ -+ case 4: /* nfsvers */ - switch (po_get_numeric(options, "nfsvers", &tmp)) { - case PO_FOUND: -- if (tmp >= 2 && tmp <= 3) { -+ if (tmp >= 2 && tmp <= 4) { - *version = tmp; - return 1; - } -diff --git a/utils/mount/network.h b/utils/mount/network.h -index db5134c..402e0a5 100644 ---- a/utils/mount/network.h -+++ b/utils/mount/network.h -@@ -56,6 +56,7 @@ int clnt_ping(struct sockaddr_in *, const unsigned long, - - struct mount_options; - -+int nfs_nfs_version(struct mount_options *options, unsigned long *version); - int nfs_options2pmap(struct mount_options *, - struct pmap *, struct pmap *); - -diff --git a/utils/mount/nfsumount.c b/utils/mount/nfsumount.c -index f81db14..c5505b1 100644 ---- a/utils/mount/nfsumount.c -+++ b/utils/mount/nfsumount.c -@@ -179,6 +179,10 @@ static int nfs_umount_do_umnt(struct mount_options *options, - return EX_FAIL; - } - -+ /* Skip UMNT call for vers=4 mounts */ -+ if (nfs_pmap.pm_vers == 4) -+ return EX_SUCCESS; -+ - *hostname = nfs_umount_hostname(options, *hostname); - if (!*hostname) { - nfs_error(_("%s: out of memory"), progname); -diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c -index a12ace7..3eb661e 100644 ---- a/utils/mount/stropts.c -+++ b/utils/mount/stropts.c -@@ -84,6 +84,7 @@ struct nfsmount_info { - struct mount_options *options; /* parsed mount options */ - char **extra_opts; /* string for /etc/mtab */ - -+ unsigned long version; /* NFS version */ - int flags, /* MS_ flags */ - fake, /* actually do the mount? */ - child; /* forked bg child? */ -@@ -272,7 +273,12 @@ static int nfs_validate_options(struct nfsmount_info *mi) - if (!nfs_name_to_address(mi->hostname, sap, &salen)) - return 0; - -- if (strncmp(mi->type, "nfs4", 4) == 0) { -+ if (!nfs_nfs_version(mi->options, &mi->version)) -+ return 0; -+ if (strncmp(mi->type, "nfs4", 4) == 0) -+ mi->version = 4; -+ -+ if (mi->version == 4) { - if (!nfs_append_clientaddr_option(sap, salen, mi->options)) - return 0; - } else { -@@ -488,7 +494,7 @@ static int nfs_try_mount(struct nfsmount_info *mi) - char *options = NULL; - int result; - -- if (strncmp(mi->type, "nfs4", 4) != 0) { -+ if (mi->version != 4) { - if (!nfs_rewrite_pmap_mount_options(mi->options)) - return 0; - } -diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c -index b59f939..888fd8c 100644 ---- a/utils/mountd/mountd.c -+++ b/utils/mountd/mountd.c -@@ -359,6 +359,11 @@ static void set_authflavors(struct mountres3_ok *ok, nfs_export *exp) - flavors[i] = s->flav->fnum; - i++; - } -+ if (i == 0) { -+ /* default when there is no sec= option: */ -+ i = 1; -+ flavors[0] = AUTH_UNIX; -+ } - ok->auth_flavors.auth_flavors_val = flavors; - ok->auth_flavors.auth_flavors_len = i; - } -diff --git a/utils/nfsd/nfsd.c b/utils/nfsd/nfsd.c -index 650c593..1cda1e5 100644 ---- a/utils/nfsd/nfsd.c -+++ b/utils/nfsd/nfsd.c -@@ -27,6 +27,15 @@ - #include "nfssvc.h" - #include "xlog.h" - -+/* -+ * IPv6 support for nfsd was finished before some of the other daemons (mountd -+ * and statd in particular). That could be a problem in the future if someone -+ * were to boot a kernel that supports IPv6 serving with an older nfs-utils. For -+ * now, hardcode the IPv6 switch into the off position until the other daemons -+ * are functional. -+ */ -+#undef IPV6_SUPPORTED -+ - static void usage(const char *); - - static struct option longopts[] = -diff --git a/utils/nfsd/nfssvc.c b/utils/nfsd/nfssvc.c -index ee862b2..12d3253 100644 ---- a/utils/nfsd/nfssvc.c -+++ b/utils/nfsd/nfssvc.c -@@ -22,6 +22,15 @@ - #include "nfslib.h" - #include "xlog.h" - -+/* -+ * IPv6 support for nfsd was finished before some of the other daemons (mountd -+ * and statd in particular). That could be a problem in the future if someone -+ * were to boot a kernel that supports IPv6 serving with an older nfs-utils. For -+ * now, hardcode the IPv6 switch into the off position until the other daemons -+ * are functional. -+ */ -+#undef IPV6_SUPPORTED -+ - #define NFSD_PORTS_FILE "/proc/fs/nfsd/portlist" - #define NFSD_VERS_FILE "/proc/fs/nfsd/versions" - #define NFSD_THREAD_FILE "/proc/fs/nfsd/threads" diff --git a/nfs-utils-1.2.1-rc6.patch b/nfs-utils-1.2.1-rc6.patch deleted file mode 100644 index d8c8a3d..0000000 --- a/nfs-utils-1.2.1-rc6.patch +++ /dev/null @@ -1,307 +0,0 @@ -diff --git a/utils/mount/parse_opt.c b/utils/mount/parse_opt.c -index 1dfee8a..f0918f7 100644 ---- a/utils/mount/parse_opt.c -+++ b/utils/mount/parse_opt.c -@@ -101,6 +101,37 @@ fail: - return NULL; - } - -+static struct mount_option *option_dup(const struct mount_option *option) -+{ -+ struct mount_option *new; -+ -+ new = malloc(sizeof(*new)); -+ if (!new) -+ return NULL; -+ -+ new->next = NULL; -+ new->prev = NULL; -+ -+ new->keyword = strdup(option->keyword); -+ if (!new->keyword) -+ goto fail; -+ -+ new->value = NULL; -+ if (option->value) { -+ new->value = strdup(option->value); -+ if (!new->value) { -+ free(new->keyword); -+ goto fail; -+ } -+ } -+ -+ return new; -+ -+fail: -+ free(new); -+ return NULL; -+} -+ - static void option_destroy(struct mount_option *option) - { - free(option->keyword); -@@ -229,6 +260,40 @@ fail: - } - - /** -+ * po_dup - duplicate an existing list of options -+ * @options: pointer to mount options -+ * -+ */ -+struct mount_options *po_dup(struct mount_options *source) -+{ -+ struct mount_options *target; -+ struct mount_option *current; -+ -+ if (!source) -+ return NULL; -+ -+ target = options_create(); -+ if (options_empty(source) || target == NULL) -+ return target; -+ -+ current = source->head; -+ while (target->count < source->count) { -+ struct mount_option *option; -+ -+ option = option_dup(current); -+ if (!option) { -+ po_destroy(target); -+ return NULL; -+ } -+ -+ options_tail_insert(target, option); -+ current = current->next; -+ } -+ -+ return target; -+} -+ -+/** - * po_replace - replace mount options in one mount_options object with another - * @target: pointer to previously instantiated object to replace - * @source: pointer to object containing source mount options -diff --git a/utils/mount/parse_opt.h b/utils/mount/parse_opt.h -index f9243c3..2c0b5f4 100644 ---- a/utils/mount/parse_opt.h -+++ b/utils/mount/parse_opt.h -@@ -38,6 +38,7 @@ typedef enum { - struct mount_options; - - struct mount_options * po_split(char *); -+struct mount_options * po_dup(struct mount_options *); - void po_replace(struct mount_options *, - struct mount_options *); - po_return_t po_join(struct mount_options *, char **); -diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c -index 3eb661e..069bdc1 100644 ---- a/utils/mount/stropts.c -+++ b/utils/mount/stropts.c -@@ -80,6 +80,8 @@ struct nfsmount_info { - *node, /* mounted-on dir */ - *type; /* "nfs" or "nfs4" */ - char *hostname; /* server's hostname */ -+ struct sockaddr_storage address; /* server's address */ -+ socklen_t salen; /* size of server's address */ - - struct mount_options *options; /* parsed mount options */ - char **extra_opts; /* string for /etc/mtab */ -@@ -257,47 +259,35 @@ static int nfs_append_sloppy_option(struct mount_options *options) - } - - /* -- * Set up mandatory NFS mount options. -+ * Set up mandatory non-version specific NFS mount options. - * - * Returns 1 if successful; otherwise zero. - */ - static int nfs_validate_options(struct nfsmount_info *mi) - { -- struct sockaddr_storage dummy; -- struct sockaddr *sap = (struct sockaddr *)&dummy; -- socklen_t salen = sizeof(dummy); -+ struct sockaddr *sap = (struct sockaddr *)&mi->address; - - if (!nfs_parse_devname(mi->spec, &mi->hostname, NULL)) - return 0; - -- if (!nfs_name_to_address(mi->hostname, sap, &salen)) -+ mi->salen = sizeof(mi->address); -+ if (!nfs_name_to_address(mi->hostname, sap, &mi->salen)) - return 0; - - if (!nfs_nfs_version(mi->options, &mi->version)) - return 0; - if (strncmp(mi->type, "nfs4", 4) == 0) - mi->version = 4; -- -- if (mi->version == 4) { -- if (!nfs_append_clientaddr_option(sap, salen, mi->options)) -- return 0; -- } else { -- if (!nfs_fix_mounthost_option(mi->options)) -- return 0; -- if (!mi->fake && !nfs_verify_lock_option(mi->options)) -- return 0; -+ else { -+ char *option = po_get(mi->options, "proto"); -+ if (option && strcmp(option, "rdma") == 0) -+ mi->version = 3; - } - - if (!nfs_append_sloppy_option(mi->options)) - return 0; - -- if (!nfs_append_addr_option(sap, salen, mi->options)) -- return 0; -- -- /* -- * Update option string to be recorded in /etc/mnttab -- */ -- if (po_join(mi->options, mi->extra_opts) == PO_FAILED) -+ if (!nfs_append_addr_option(sap, mi->salen, mi->options)) - return 0; - - return 1; -@@ -489,17 +479,12 @@ out: - * Returns TRUE if successful, otherwise FALSE. - * "errno" is set to reflect the individual error. - */ --static int nfs_try_mount(struct nfsmount_info *mi) -+static int nfs_sys_mount(struct nfsmount_info *mi, struct mount_options *opts) - { - char *options = NULL; - int result; - -- if (mi->version != 4) { -- if (!nfs_rewrite_pmap_mount_options(mi->options)) -- return 0; -- } -- -- if (po_join(mi->options, &options) == PO_FAILED) { -+ if (po_join(opts, &options) == PO_FAILED) { - errno = EIO; - return 0; - } -@@ -522,6 +507,121 @@ static int nfs_try_mount(struct nfsmount_info *mi) - } - - /* -+ * For "-t nfs vers=2" or "-t nfs vers=3" mounts. -+ */ -+static int nfs_try_mount_v3v2(struct nfsmount_info *mi) -+{ -+ struct mount_options *options = po_dup(mi->options); -+ int result = 0; -+ -+ if (!options) { -+ errno = ENOMEM; -+ return result; -+ } -+ -+ if (!nfs_fix_mounthost_option(options)) { -+ errno = EINVAL; -+ goto out_fail; -+ } -+ if (!mi->fake && !nfs_verify_lock_option(options)) { -+ errno = EINVAL; -+ goto out_fail; -+ } -+ -+ /* -+ * Options we negotiate below may be stale by the time this -+ * file system is unmounted. In order to force umount.nfs -+ * to renegotiate with the server, only write the user- -+ * specified options, and not negotiated options, to /etc/mtab. -+ */ -+ if (po_join(options, mi->extra_opts) == PO_FAILED) { -+ errno = ENOMEM; -+ goto out_fail; -+ } -+ -+ if (!nfs_rewrite_pmap_mount_options(options)) -+ goto out_fail; -+ -+ result = nfs_sys_mount(mi, options); -+ -+out_fail: -+ po_destroy(options); -+ return result; -+} -+ -+/* -+ * For "-t nfs -o vers=4" or "-t nfs4" mounts. -+ */ -+static int nfs_try_mount_v4(struct nfsmount_info *mi) -+{ -+ struct sockaddr *sap = (struct sockaddr *)&mi->address; -+ struct mount_options *options = po_dup(mi->options); -+ int result = 0; -+ -+ if (!options) { -+ errno = ENOMEM; -+ return result; -+ } -+ -+ if (mi->version == 0) { -+ if (po_append(options, "vers=4") == PO_FAILED) { -+ errno = EINVAL; -+ goto out_fail; -+ } -+ } -+ -+ if (!nfs_append_clientaddr_option(sap, mi->salen, options)) { -+ errno = EINVAL; -+ goto out_fail; -+ } -+ /* -+ * Update option string to be recorded in /etc/mtab. -+ */ -+ if (po_join(options, mi->extra_opts) == PO_FAILED) { -+ errno = ENOMEM; -+ return 0; -+ } -+ -+ result = nfs_sys_mount(mi, options); -+ -+out_fail: -+ po_destroy(options); -+ return result; -+} -+ -+/* -+ * This is a single pass through the fg/bg loop. -+ * -+ * Returns TRUE if successful, otherwise FALSE. -+ * "errno" is set to reflect the individual error. -+ */ -+static int nfs_try_mount(struct nfsmount_info *mi) -+{ -+ int result = 0; -+ -+ switch (mi->version) { -+ case 0: -+ if (linux_version_code() > MAKE_VERSION(2, 6, 31)) { -+ errno = 0; -+ result = nfs_try_mount_v4(mi); -+ if (errno != EPROTONOSUPPORT) -+ break; -+ } -+ case 2: -+ case 3: -+ result = nfs_try_mount_v3v2(mi); -+ break; -+ case 4: -+ result = nfs_try_mount_v4(mi); -+ break; -+ default: -+ errno = EIO; -+ } -+ -+ return result; -+} -+ -+/* - * Distinguish between permanent and temporary errors. - * - * Basically, we retry if communication with the server has diff --git a/nfs-utils-1.2.1-rc7.patch b/nfs-utils-1.2.1-rc7.patch deleted file mode 100644 index 4d59118..0000000 --- a/nfs-utils-1.2.1-rc7.patch +++ /dev/null @@ -1,261 +0,0 @@ -diff --git a/support/include/conffile.h b/support/include/conffile.h -index 672020a..fe23ec2 100644 ---- a/support/include/conffile.h -+++ b/support/include/conffile.h -@@ -75,4 +75,11 @@ static inline void upper2lower(char *str) - while ((c = tolower(*str))) - *str++ = c; - } -+ -+/* -+ * Default Mount options -+ */ -+extern unsigned long config_default_vers; -+extern unsigned long config_default_proto; -+ - #endif /* _CONFFILE_H_ */ -diff --git a/utils/mount/configfile.c b/utils/mount/configfile.c -index e347b0e..28b722c 100644 ---- a/utils/mount/configfile.c -+++ b/utils/mount/configfile.c -@@ -20,13 +20,19 @@ - #include - #endif - #include -+#include -+#include - - #include - #include - #include - #include -+#include - - #include "xlog.h" -+#include "mount.h" -+#include "parse_opt.h" -+#include "network.h" - #include "conffile.h" - - #define KBYTES(x) ((x) * (1024)) -@@ -185,6 +191,63 @@ void free_all(void) - free(entry); - } - } -+static char *versions[] = {"v2", "v3", "v4", "vers", "nfsvers", NULL}; -+int inline check_vers(char *mopt, char *field) -+{ -+ int i; -+ -+ if (strncmp("mountvers", field, strlen("mountvers")) != 0) { -+ for (i=0; versions[i]; i++) -+ if (strcasestr(mopt, versions[i]) != NULL) -+ return 1; -+ } -+ return 0; -+} -+ -+unsigned long config_default_vers; -+unsigned long config_default_proto; -+/* -+ * Check to see if a default value is being set. -+ * If so, set the appropriate global value which will -+ * be used as the initial value in the server negation. -+ */ -+int inline default_value(char *mopt) -+{ -+ struct mount_options *options = NULL; -+ int dftlen = strlen("default"); -+ char *field; -+ -+ if (strncasecmp(mopt, "default", dftlen) != 0) -+ return 0; -+ -+ field = mopt + dftlen; -+ if (strncasecmp(field, "proto", strlen("proto")) == 0) { -+ if ((options = po_split(field)) != NULL) { -+ if (!nfs_nfs_protocol(options, &config_default_proto)) { -+ xlog_warn("Unable to set default protocol : %s", -+ strerror(errno)); -+ } -+ } else { -+ xlog_warn("Unable to alloc memory for default protocol"); -+ } -+ } else if (strncasecmp(field, "vers", strlen("vers")) == 0) { -+ if ((options = po_split(field)) != NULL) { -+ if (!nfs_nfs_version(options, &config_default_vers)) { -+ xlog_warn("Unable to set default version: %s", -+ strerror(errno)); -+ -+ } -+ } else { -+ xlog_warn("Unable to alloc memory for default version"); -+ } -+ } else -+ xlog_warn("Invalid default setting: '%s'", mopt); -+ -+ if (options) -+ po_destroy(options); -+ -+ return 1; -+} - /* - * Parse the given section of the configuration - * file to if there are any mount options set. -@@ -207,6 +270,12 @@ conf_parse_mntopts(char *section, char *arg, char *opts) - snprintf(buf, BUFSIZ, "%s=", node->field); - if (opts && strcasestr(opts, buf) != NULL) - continue; -+ /* -+ * Protocol verions can be set in a number of ways -+ */ -+ if (opts && check_vers(opts, node->field)) -+ continue; -+ - if (lookup_entry(node->field) != NULL) - continue; - buf[0] = '\0'; -@@ -302,15 +371,19 @@ char *conf_get_mntopts(char *spec, char *mount_point, - free_all(); - return mount_opts; - } -+ - if (mount_opts) { - strcpy(config_opts, mount_opts); - strcat(config_opts, ","); - } - SLIST_FOREACH(entry, &head, entries) { -+ if (default_value(entry->opt)) -+ continue; - strcat(config_opts, entry->opt); - strcat(config_opts, ","); - } -- *(strrchr(config_opts, ',')) = '\0'; -+ if ((ptr = strrchr(config_opts, ',')) != NULL) -+ *ptr = '\0'; - - free_all(); - if (mount_opts) -diff --git a/utils/mount/network.c b/utils/mount/network.c -index bd621be..e651167 100644 ---- a/utils/mount/network.c -+++ b/utils/mount/network.c -@@ -50,6 +50,7 @@ - #include "nfsrpc.h" - #include "parse_opt.h" - #include "network.h" -+#include "conffile.h" - - #define PMAP_TIMEOUT (10) - #define CONNECT_TIMEOUT (20) -@@ -609,10 +610,19 @@ static int nfs_probe_nfsport(const struct sockaddr *sap, const socklen_t salen, - if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port) - return 1; - -- if (nfs_mount_data_version >= 4) -+ if (nfs_mount_data_version >= 4) { -+ const unsigned int *probe_proto = probe_tcp_first; -+ -+ /* -+ * If the default proto has been set and -+ * its not TCP, start with UDP -+ */ -+ if (config_default_proto && config_default_proto != IPPROTO_TCP) -+ probe_proto = probe_udp_first; -+ - return nfs_probe_port(sap, salen, pmap, -- probe_nfs3_first, probe_tcp_first); -- else -+ probe_nfs3_first, probe_proto); -+ } else - return nfs_probe_port(sap, salen, pmap, - probe_nfs2_only, probe_udp_only); - } -@@ -1261,7 +1271,7 @@ nfs_nfs_version(struct mount_options *options, unsigned long *version) - * Returns TRUE if @protocol contains a valid value for this option, - * or FALSE if the option was specified with an invalid value. - */ --static int -+int - nfs_nfs_protocol(struct mount_options *options, unsigned long *protocol) - { - char *option; -diff --git a/utils/mount/network.h b/utils/mount/network.h -index 402e0a5..7eb89b0 100644 ---- a/utils/mount/network.h -+++ b/utils/mount/network.h -@@ -57,6 +57,8 @@ int clnt_ping(struct sockaddr_in *, const unsigned long, - struct mount_options; - - int nfs_nfs_version(struct mount_options *options, unsigned long *version); -+int nfs_nfs_protocol(struct mount_options *options, unsigned long *protocol); -+ - int nfs_options2pmap(struct mount_options *, - struct pmap *, struct pmap *); - -diff --git a/utils/mount/nfsmount.conf b/utils/mount/nfsmount.conf -index f9fcfcb..9b8ff4a 100644 ---- a/utils/mount/nfsmount.conf -+++ b/utils/mount/nfsmount.conf -@@ -24,14 +24,29 @@ - # All reads and writes to the 'nfsserver.foo.com' server - # will be done with 32k (32768 bytes) block sizes. - # --#[ NFSMount_Global_Options ] -+[ NFSMount_Global_Options ] - # This statically named section defines global mount - # options that can be applied on all NFS mount. - # --# Protocol Version [2,3] --# Nfsvers=3 --# Network Transport [Udp,Tcp,Rdma] --# Proto=Tcp -+# Protocol Version [2,3,4] -+# This defines the default protocol version which will -+# be used to start the negotiation with the server. -+# Defaultvers=4 -+# -+# Setting this option makes it mandatory the server supports the -+# given version. The mount will fail if the given version is -+# not support by the server. -+# Nfsvers=4 -+# -+# Network Protocol [udp,tcp,rdma] (Note: values are case sensitive) -+# This defines the default network protocol which will -+# be used to start the negotiation with the server. -+# Defaultproto=tcp -+# -+# Setting this option makes it mandatory the server supports the -+# given network protocol. The mount will fail if the given network -+# protocol is not supported by the server. -+# Proto=tcp - # - # The number of times a request will be retired before - # generating a timeout -diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c -index 069bdc1..ceefdb0 100644 ---- a/utils/mount/stropts.c -+++ b/utils/mount/stropts.c -@@ -45,6 +45,7 @@ - #include "parse_opt.h" - #include "version.h" - #include "parse_dev.h" -+#include "conffile.h" - - #ifndef NFS_PROGRAM - #define NFS_PROGRAM (100003) -@@ -283,6 +284,14 @@ static int nfs_validate_options(struct nfsmount_info *mi) - if (option && strcmp(option, "rdma") == 0) - mi->version = 3; - } -+ /* -+ * Use the default value set in the config file when -+ * the version has not been explicitly set. -+ */ -+ if (mi->version == 0 && config_default_vers) { -+ if (config_default_vers < 4) -+ mi->version = config_default_vers; -+ } - - if (!nfs_append_sloppy_option(mi->options)) - return 0; diff --git a/nfs-utils.spec b/nfs-utils.spec index df96c10..8ece9d1 100644 --- a/nfs-utils.spec +++ b/nfs-utils.spec @@ -1,8 +1,8 @@ Summary: NFS utilities and supporting clients and daemons for the kernel NFS server Name: nfs-utils URL: http://sourceforge.net/projects/nfs -Version: 1.2.0 -Release: 19%{?dist} +Version: 1.2.1 +Release: 1%{?dist} Epoch: 1 # group all 32bit related archs @@ -22,19 +22,10 @@ Patch00: nfs-utils-1.0.5-statdpath.patch Patch01: nfs-utils-1.1.0-smnotify-path.patch Patch02: nfs-utils-1.1.0-exp-subtree-warn-off.patch -Patch100: nfs-utils-1.2.1-rc1.patch -Patch101: nfs-utils-1.2.1-rc2.patch -Patch102: nfs-utils-1.2.1-rc3.patch -Patch103: nfs-utils-1.2.1-rc4.patch -Patch104: nfs-utils-1.2.1-rc5.patch -Patch105: nfs-utils-1.2.1-rc6.patch -Patch106: nfs-utils-1.2.1-rc7.patch - -Patch200: nfs-utils-1.2.0-v4root-rel7.patch +Patch200: nfs-utils-1.2.0-v4root-rel8.patch Patch201: nfs-utils-1.2.0-mount-default.patch Patch202: nfs-utils-1.2.1-nfsd-bootfail.patch - Group: System Environment/Daemons Provides: exportfs = %{epoch}:%{version}-%{release} Provides: nfsstat = %{epoch}:%{version}-%{release} @@ -85,14 +76,6 @@ This package also contains the mount.nfs and umount.nfs program. %patch01 -p1 %patch02 -p1 -%patch100 -p1 -%patch101 -p1 -%patch102 -p1 -%patch103 -p1 -%patch104 -p1 -%patch105 -p1 -%patch106 -p1 - %patch200 -p1 %patch201 -p1 %patch202 -p1 @@ -267,7 +250,9 @@ fi %attr(4755,root,root) /sbin/umount.nfs4 %changelog -* Thu Nov 12 2009 Steve Dickson 1.2.0-19 +* Fri Nov 13 2009 Steve Dickson 1.2.1-1 +- Updated to latest upstream release 1.2.1 +- Updated to the latest pseudo root release (rel8). - Stop rpc.nfsd from failing to startup when the network is down (bz 532270) diff --git a/sources b/sources index 6618483..e52d658 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ ae7db9c61c5ad04f83bb99e5caed73da nfs.doc.tar.gz -779cf81044e92cb51ad590960e7b3671 nfs-utils-1.2.0.tar.bz2 +c3ccd16c147befd49fe4541a506dd177 nfs-utils-1.2.1.tar.bz2