diff --git a/.gitignore b/.gitignore index 4bff9e3..f5b5cf0 100644 --- a/.gitignore +++ b/.gitignore @@ -64,6 +64,7 @@ tests/nsm_client/nlm_sm_inter.h tests/nsm_client/nlm_sm_inter_clnt.c tests/nsm_client/nlm_sm_inter_svc.c tests/nsm_client/nlm_sm_inter_xdr.c +utils/nfsidmap/nfsidmap # cscope database files cscope.* # generic editor backup et al diff --git a/aclocal/keyutils.m4 b/aclocal/keyutils.m4 new file mode 100644 index 0000000..84bc112 --- /dev/null +++ b/aclocal/keyutils.m4 @@ -0,0 +1,11 @@ +dnl Checks for keyutils library and headers +dnl +AC_DEFUN([AC_KEYUTILS], [ + + dnl Check for libkeyutils; do not add to LIBS if found + AC_CHECK_LIB([keyutils], [keyctl_instantiate], [LIBKEYUTILS=-lkeyutils], ,) + AC_SUBST(LIBKEYUTILS) + + AC_CHECK_HEADERS([keyutils.h], , + [AC_MSG_ERROR([keyutils.h header not found.])]) +])dnl diff --git a/aclocal/libnfsidmap.m4 b/aclocal/libnfsidmap.m4 index cfcde2f..4faa923 100644 --- a/aclocal/libnfsidmap.m4 +++ b/aclocal/libnfsidmap.m4 @@ -14,4 +14,8 @@ AC_DEFUN([AC_LIBNFSIDMAP], [ [AC_DEFINE([HAVE_NFS4_SET_DEBUG], 1, [Define to 1 if you have the `nfs4_set_debug' function.])]) + dnl only enable nfsidmap when libnfsidmap supports it + AC_CHECK_LIB([nfsidmap], [nfs4_owner_to_uid], [enable_nfsidmap=yes], + [enable_nfsidmap=no]) + ])dnl diff --git a/configure.ac b/configure.ac index 3058be6..d8972e4 100644 --- a/configure.ac +++ b/configure.ac @@ -132,11 +132,20 @@ AC_ARG_ENABLE(mount, enable_mount=$enableval, enable_mount=yes) AM_CONDITIONAL(CONFIG_MOUNT, [test "$enable_mount" = "yes"]) + +if test "$enable_mount" = yes; then + AC_ARG_ENABLE(libmount-mount, + [AC_HELP_STRING([--enable-libmount-mount], + [Link mount.nfs with libmount (EXPERIMENTAL)])], + enable_libmount=yes, + enable_libmount=no) +fi + AC_ARG_ENABLE(tirpc, [AC_HELP_STRING([--enable-tirpc], [enable use of TI-RPC @<:@default=yes@:>@])], enable_tirpc=$enableval, - enable_tirpc='yes') + enable_tirpc='') AC_ARG_ENABLE(ipv6, [AC_HELP_STRING([--enable-ipv6], [enable support for IPv6 @<:@default=no@:>@])], @@ -247,6 +256,12 @@ if test "$enable_nfsv4" = yes; then dnl check for nfsidmap libraries and headers AC_LIBNFSIDMAP + dnl enable nfsidmap when its support by libnfsidmap + AM_CONDITIONAL(CONFIG_NFSIDMAP, [test "$enable_nfsidmap" = "yes"]) + + dnl check for the keyutils libraries and headers + AC_KEYUTILS + dnl librpcsecgss already has a dependency on libgssapi, dnl but we need to make sure we get the right version if test "$enable_gss" = yes; then @@ -279,6 +294,13 @@ AC_SUBST(LIBCRYPT) AC_SUBST(LIBBSD) AC_SUBST(LIBBLKID) +if test "$enable_libmount" != no; then + AC_CHECK_LIB(mount, mnt_context_do_mount, [LIBMOUNT="-lmount"], AC_MSG_ERROR([libmount needed])) + AC_CHECK_HEADER(libmount/libmount.h, , AC_MSG_ERROR([Cannot find libmount header file libmount/libmount.h])) +fi +AM_CONDITIONAL(CONFIG_LIBMOUNT, [test "$enable_libmount" = "yes"]) +AC_SUBST(LIBMOUNT) + if test "$enable_gss" = yes; then dnl 'gss' requires getnameinfo - at least for gssd_proc.c AC_CHECK_FUNC([getnameinfo], , [AC_MSG_ERROR([GSSAPI support requires 'getnameinfo' function])]) @@ -435,6 +457,7 @@ AC_CONFIG_FILES([ utils/mountd/Makefile utils/nfsd/Makefile utils/nfsstat/Makefile + utils/nfsidmap/Makefile utils/showmount/Makefile utils/statd/Makefile tests/Makefile diff --git a/support/export/client.c b/support/export/client.c index dbfc2b1..ba2db8f 100644 --- a/support/export/client.c +++ b/support/export/client.c @@ -178,6 +178,7 @@ out_badprefix: static int init_netmask6(nfs_client *UNUSED(clp), const char *UNUSED(slash)) { + return 0; } #endif /* IPV6_SUPPORTED */ diff --git a/support/export/export.c b/support/export/export.c index f528603..4fda30a 100644 --- a/support/export/export.c +++ b/support/export/export.c @@ -38,6 +38,7 @@ export_free(nfs_export *exp) xfree(exp->m_export.e_sqgids); free(exp->m_export.e_mountpoint); free(exp->m_export.e_fslocdata); + free(exp->m_export.e_uuid); xfree(exp->m_export.e_hostname); xfree(exp); diff --git a/support/export/hostname.c b/support/export/hostname.c index 3c55ce7..efcb75c 100644 --- a/support/export/hostname.c +++ b/support/export/hostname.c @@ -30,10 +30,6 @@ #include "sockaddr.h" #include "exportfs.h" -#ifndef HAVE_DECL_AI_ADDRCONFIG -#define AI_ADDRCONFIG 0 -#endif - /** * host_ntop - generate presentation address given a sockaddr * @sap: pointer to socket address @@ -170,7 +166,7 @@ host_addrinfo(const char *hostname) #endif /* don't return duplicates */ .ai_protocol = (int)IPPROTO_UDP, - .ai_flags = AI_ADDRCONFIG | AI_CANONNAME, + .ai_flags = AI_CANONNAME, }; int error; diff --git a/support/include/exportfs.h b/support/include/exportfs.h index 3cf1ee8..01e87dd 100644 --- a/support/include/exportfs.h +++ b/support/include/exportfs.h @@ -100,6 +100,7 @@ typedef struct mexport { } nfs_export; #define HASH_TABLE_SIZE 1021 +#define DEFAULT_TTL (30 * 60) typedef struct _exp_hash_entry { nfs_export * p_first; diff --git a/support/include/nfslib.h b/support/include/nfslib.h index 3db5bec..73f3c20 100644 --- a/support/include/nfslib.h +++ b/support/include/nfslib.h @@ -25,6 +25,12 @@ #ifndef _PATH_EXPORTS #define _PATH_EXPORTS "/etc/exports" #endif +#ifndef _PATH_EXPORTS_D +#define _PATH_EXPORTS_D "/etc/exports.d" +#endif +#ifndef _EXT_EXPORT +#define _EXT_EXPORT ".exports" +#endif #ifndef _PATH_IDMAPDCONF #define _PATH_IDMAPDCONF "/etc/idmapd.conf" #endif @@ -89,6 +95,7 @@ struct exportent { char * e_fslocdata; char * e_uuid; struct sec_entry e_secinfo[SECFLAVOR_COUNT+1]; + unsigned int e_ttl; }; struct rmtabent { @@ -163,6 +170,12 @@ void closeall(int min); int svctcp_socket (u_long __number, int __reuse); int svcudp_socket (u_long __number); +/* Misc shared code prototypes */ +size_t strlcat(char *, const char *, size_t); +size_t strlcpy(char *, const char *, size_t); +ssize_t atomicio(ssize_t (*f) (int, void*, size_t), + int, void *, size_t); + #define UNUSED(x) UNUSED_ ## x __attribute__((unused)) diff --git a/support/include/rpcmisc.h b/support/include/rpcmisc.h index c5847fa..0b06457 100644 --- a/support/include/rpcmisc.h +++ b/support/include/rpcmisc.h @@ -31,7 +31,7 @@ struct rpc_dentry { struct rpc_dtable { struct rpc_dentry *entries; - int nproc; + rpcproc_t nproc; }; #define dtable_ent(func, vers, arg_type, res_type) \ diff --git a/support/nfs/Makefile.am b/support/nfs/Makefile.am index 60400b2..05c2fc4 100644 --- a/support/nfs/Makefile.am +++ b/support/nfs/Makefile.am @@ -5,7 +5,7 @@ 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 conffile.c \ - svc_create.c + svc_create.c atomicio.c strlcpy.c strlcat.c MAINTAINERCLEANFILES = Makefile.in diff --git a/support/nfs/atomicio.c b/support/nfs/atomicio.c new file mode 100644 index 0000000..5e760e6 --- /dev/null +++ b/support/nfs/atomicio.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2002 Marius Aamodt Eriksen + * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved. + * 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. + */ + +#include +#include +#include + +/* + * ensure all of data on socket comes through. f==read || f==write + */ +ssize_t atomicio(ssize_t(*f) (int, void *, size_t), int fd, void *_s, size_t n) +{ + char *s = _s; + ssize_t res, pos = 0; + + while ((ssize_t)n > pos) { + res = (f) (fd, s + pos, n - pos); + switch (res) { + case -1: + if (errno == EINTR || errno == EAGAIN) + continue; + case 0: + if (pos != 0) + return pos; + return res; + default: + pos += res; + } + } + return pos; +} diff --git a/support/nfs/conffile.c b/support/nfs/conffile.c index 24640f4..fa0dc6b 100644 --- a/support/nfs/conffile.c +++ b/support/nfs/conffile.c @@ -251,6 +251,7 @@ conf_parse_line(int trans, char *line, size_t sz) } /* Strip off any blanks before ']' */ val = line; + j=0; while (*val && !isblank(*val)) val++, j++; if (*val) @@ -271,9 +272,9 @@ conf_parse_line(int trans, char *line, size_t sz) if (ptr == NULL) return; line = ++ptr; - while (*ptr && *ptr != '"') + while (*ptr && *ptr != '"' && *ptr != ']') ptr++; - if (*ptr == '\0') { + if (*ptr == '\0' || *ptr == ']') { xlog_warn("config file error: line %d: " "non-matched '\"', ignoring until next section", ln); } else { diff --git a/support/nfs/exports.c b/support/nfs/exports.c index a93941c..6acb2b6 100644 --- a/support/nfs/exports.c +++ b/support/nfs/exports.c @@ -107,6 +107,7 @@ static void init_exportent (struct exportent *ee, int fromkernel) ee->e_nsquids = 0; ee->e_nsqgids = 0; ee->e_uuid = NULL; + ee->e_ttl = DEFAULT_TTL; } struct exportent * @@ -332,6 +333,8 @@ dupexportent(struct exportent *dst, struct exportent *src) dst->e_mountpoint = strdup(src->e_mountpoint); if (src->e_fslocdata) dst->e_fslocdata = strdup(src->e_fslocdata); + if (src->e_uuid) + dst->e_uuid = strdup(src->e_uuid); dst->e_hostname = NULL; } diff --git a/support/nfs/rpcdispatch.c b/support/nfs/rpcdispatch.c index 984c646..f7c27c9 100644 --- a/support/nfs/rpcdispatch.c +++ b/support/nfs/rpcdispatch.c @@ -32,7 +32,12 @@ rpc_dispatch(struct svc_req *rqstp, SVCXPRT *transp, return; } dtable += (rqstp->rq_vers - 1); - if (((int)rqstp->rq_proc) > dtable->nproc) { + if (rqstp->rq_proc > dtable->nproc) { + svcerr_noproc(transp); + return; + } + + if (dtable->nproc <= rqstp->rq_proc) { svcerr_noproc(transp); return; } diff --git a/support/nfs/strlcat.c b/support/nfs/strlcat.c new file mode 100644 index 0000000..daedd7a --- /dev/null +++ b/support/nfs/strlcat.c @@ -0,0 +1,76 @@ +/* $OpenBSD: strlcat.c,v 1.8 2001/05/13 15:40:15 deraadt Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = "$OpenBSD: strlcat.c,v 1.8 2001/05/13 15:40:15 deraadt Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. + */ +size_t +strlcat(char *dst, + const char *src, + size_t siz) +{ + register char *d = dst; + register const char *s = src; + register size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} diff --git a/support/nfs/strlcpy.c b/support/nfs/strlcpy.c new file mode 100644 index 0000000..a2653ee --- /dev/null +++ b/support/nfs/strlcpy.c @@ -0,0 +1,72 @@ +/* $OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = "$OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +strlcpy(char *dst, + const char *src, + size_t siz) +{ + register char *d = dst; + register const char *s = src; + register size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0 && --n != 0) { + do { + if ((*d++ = *s++) == 0) + break; + } while (--n != 0); + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} diff --git a/support/nfs/svc_create.c b/support/nfs/svc_create.c index 59ba505..b3f75ed 100644 --- a/support/nfs/svc_create.c +++ b/support/nfs/svc_create.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -41,11 +42,68 @@ #include "tcpwrapper.h" #endif +#include "sockaddr.h" #include "rpcmisc.h" #include "xlog.h" #ifdef HAVE_LIBTIRPC +#define SVC_CREATE_XPRT_CACHE_SIZE (8) +static SVCXPRT *svc_create_xprt_cache[SVC_CREATE_XPRT_CACHE_SIZE] = { NULL, }; + +/* + * Cache an SVC xprt, in case there are more programs or versions to + * register against it. + */ +static void +svc_create_cache_xprt(SVCXPRT *xprt) +{ + unsigned int i; + + /* Check if we've already got this one... */ + for (i = 0; i < SVC_CREATE_XPRT_CACHE_SIZE; i++) + if (svc_create_xprt_cache[i] == xprt) + return; + + /* No, we don't. Cache it. */ + for (i = 0; i < SVC_CREATE_XPRT_CACHE_SIZE; i++) + if (svc_create_xprt_cache[i] == NULL) { + svc_create_xprt_cache[i] = xprt; + return; + } + + xlog(L_ERROR, "%s: Failed to cache an xprt", __func__); +} + +/* + * Find a previously cached SVC xprt structure with the given bind address + * and transport semantics. + * + * Returns pointer to a cached SVC xprt. + * + * If no matching SVC XPRT can be found, NULL is returned. + */ +static SVCXPRT * +svc_create_find_xprt(const struct sockaddr *bindaddr, const struct netconfig *nconf) +{ + unsigned int i; + + for (i = 0; i < SVC_CREATE_XPRT_CACHE_SIZE; i++) { + SVCXPRT *xprt = svc_create_xprt_cache[i]; + struct sockaddr *sap; + + if (xprt == NULL) + continue; + if (strcmp(nconf->nc_netid, xprt->xp_netid) != 0) + continue; + sap = (struct sockaddr *)xprt->xp_ltaddr.buf; + if (!nfs_compare_sockaddr(bindaddr, sap)) + continue; + return xprt; + } + return NULL; +} + /* * Set up an appropriate bind address, given @port and @nconf. * @@ -98,17 +156,113 @@ svc_create_bindaddr(struct netconfig *nconf, const uint16_t port) return ai; } +/* + * Create a listener socket on a specific bindaddr, and set + * special socket options to allow it to share the same port + * as other listeners. + * + * Returns an open, bound, and possibly listening network + * socket on success. + * + * Otherwise returns -1 if some error occurs. + */ +static int +svc_create_sock(const struct sockaddr *sap, socklen_t salen, + struct netconfig *nconf) +{ + int fd, type, protocol; + int one = 1; + + switch(nconf->nc_semantics) { + case NC_TPI_CLTS: + type = SOCK_DGRAM; + break; + case NC_TPI_COTS_ORD: + type = SOCK_STREAM; + break; + default: + xlog(D_GENERAL, "%s: Unrecognized bind address semantics: %u", + __func__, nconf->nc_semantics); + return -1; + } + + if (strcmp(nconf->nc_proto, NC_UDP) == 0) + protocol = (int)IPPROTO_UDP; + else if (strcmp(nconf->nc_proto, NC_TCP) == 0) + protocol = (int)IPPROTO_TCP; + else { + xlog(D_GENERAL, "%s: Unrecognized bind address protocol: %s", + __func__, nconf->nc_proto); + return -1; + } + + fd = socket((int)sap->sa_family, type, protocol); + if (fd == -1) { + xlog(L_ERROR, "Could not make a socket: (%d) %m", + errno); + return -1; + } + +#ifdef IPV6_SUPPORTED + if (sap->sa_family == AF_INET6) { + if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, + &one, sizeof(one)) == -1) { + xlog(L_ERROR, "Failed to set IPV6_V6ONLY: (%d) %m", + errno); + (void)close(fd); + return -1; + } + } +#endif /* IPV6_SUPPORTED */ + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, + &one, sizeof(one)) == -1) { + xlog(L_ERROR, "Failed to set SO_REUSEADDR: (%d) %m", + errno); + (void)close(fd); + return -1; + } + + if (bind(fd, sap, salen) == -1) { + xlog(L_ERROR, "Could not bind socket: (%d) %m", + errno); + (void)close(fd); + return -1; + } + + if (nconf->nc_semantics == NC_TPI_COTS_ORD) + if (listen(fd, SOMAXCONN) == -1) { + xlog(L_ERROR, "Could not listen on socket: (%d) %m", + errno); + (void)close(fd); + return -1; + } + + return fd; +} + +/* + * The simple case is allowing the TI-RPC library to create a + * transport itself, given just the bind address and transport + * semantics. + * + * Our local xprt cache is ignored in this path, since the + * caller is not interested in sharing listeners or ports, and + * the library automatically avoids ports already in use. + * + * Returns the count of started listeners (one or zero). + */ static unsigned int -svc_create_nconf(const char *name, const rpcprog_t program, +svc_create_nconf_rand_port(const char *name, const rpcprog_t program, const rpcvers_t version, void (*dispatch)(struct svc_req *, SVCXPRT *), - const uint16_t port, struct netconfig *nconf) + struct netconfig *nconf) { struct t_bind bindaddr; struct addrinfo *ai; SVCXPRT *xprt; - ai = svc_create_bindaddr(nconf, port); + ai = svc_create_bindaddr(nconf, 0); if (ai == NULL) return 0; @@ -119,7 +273,7 @@ svc_create_nconf(const char *name, const rpcprog_t program, freeaddrinfo(ai); if (xprt == NULL) { xlog(D_GENERAL, "Failed to create listener xprt " - "(%s, %u, %s)", name, version, nconf->nc_netid); + "(%s, %u, %s)", name, version, nconf->nc_netid); return 0; } @@ -133,6 +287,93 @@ svc_create_nconf(const char *name, const rpcprog_t program, return 1; } +/* + * If a port is specified on the command line, that port value will be + * the same for all listeners created here. Create each listener + * socket in advance and set SO_REUSEADDR, rather than allowing the + * RPC library to create the listeners for us on a randomly chosen + * port via svc_tli_create(RPC_ANYFD). + * + * Some callers want to listen for more than one RPC version using the + * same port number. For example, mountd could want to listen for MNT + * version 1, 2, and 3 requests. This means mountd must use the same + * set of listener sockets for multiple RPC versions, since, on one + * system, you can't have two listener sockets with the exact same + * bind address (and port) and transport protocol. + * + * To accomplish this, this function caches xprts as they are created. + * This cache is checked to see if a previously created xprt can be + * used, before creating a new xprt for this [program, version]. If + * there is a cached xprt with the same bindaddr and transport + * semantics, we simply register the new version with that xprt, + * rather than creating a fresh xprt for it. + * + * The xprt cache implemented here is local to a process. Two + * separate RPC daemons can not share a set of listeners. + * + * Returns the count of started listeners (one or zero). + */ +static unsigned int +svc_create_nconf_fixed_port(const char *name, const rpcprog_t program, + const rpcvers_t version, + void (*dispatch)(struct svc_req *, SVCXPRT *), + const uint16_t port, struct netconfig *nconf) +{ + struct addrinfo *ai; + SVCXPRT *xprt; + + ai = svc_create_bindaddr(nconf, port); + if (ai == NULL) + return 0; + + xprt = svc_create_find_xprt(ai->ai_addr, nconf); + if (xprt == NULL) { + int fd; + + fd = svc_create_sock(ai->ai_addr, ai->ai_addrlen, nconf); + if (fd == -1) + goto out_free; + + xprt = svc_tli_create(fd, nconf, NULL, 0, 0); + if (xprt == NULL) { + xlog(D_GENERAL, "Failed to create listener xprt " + "(%s, %u, %s)", name, version, nconf->nc_netid); + (void)close(fd); + goto out_free; + } + } + + if (!svc_reg(xprt, program, version, dispatch, nconf)) { + /* svc_reg(3) destroys @xprt in this case */ + xlog(D_GENERAL, "Failed to register (%s, %u, %s)", + name, version, nconf->nc_netid); + goto out_free; + } + + svc_create_cache_xprt(xprt); + + freeaddrinfo(ai); + return 1; + +out_free: + freeaddrinfo(ai); + return 0; +} + +static unsigned int +svc_create_nconf(const char *name, const rpcprog_t program, + const rpcvers_t version, + void (*dispatch)(struct svc_req *, SVCXPRT *), + const uint16_t port, struct netconfig *nconf) +{ + if (port != 0) + return svc_create_nconf_fixed_port(name, program, + version, dispatch, port, nconf); + + return svc_create_nconf_rand_port(name, program, + version, dispatch, nconf); +} + /** * nfs_svc_create - start up RPC svc listeners * @name: C string containing name of new service @@ -145,8 +386,7 @@ svc_create_nconf(const char *name, const rpcprog_t program, * the RPC dispatcher. Returns the number of started network transports. */ unsigned int -nfs_svc_create(__attribute__((unused)) char *name, - const rpcprog_t program, const rpcvers_t version, +nfs_svc_create(char *name, const rpcprog_t program, const rpcvers_t version, void (*dispatch)(struct svc_req *, SVCXPRT *), const uint16_t port) { diff --git a/support/nsm/file.c b/support/nsm/file.c index f4baeb9..98b47bf 100644 --- a/support/nsm/file.c +++ b/support/nsm/file.c @@ -126,7 +126,7 @@ exact_error_check(const ssize_t len, const size_t buflen) * containing an appropriate pathname, or NULL if an error * occurs. Caller must free the returned result with free(3). */ -__attribute_malloc__ +__attribute__((__malloc__)) static char * nsm_make_record_pathname(const char *directory, const char *hostname) { @@ -174,7 +174,7 @@ nsm_make_record_pathname(const char *directory, const char *hostname) * containing an appropriate pathname, or NULL if an error * occurs. Caller must free the returned result with free(3). */ -__attribute_malloc__ +__attribute__((__malloc__)) static char * nsm_make_pathname(const char *directory) { @@ -204,7 +204,7 @@ nsm_make_pathname(const char *directory) * containing an appropriate pathname, or NULL if an error * occurs. Caller must free the returned result with free(3). */ -__attribute_malloc__ +__attribute__((__malloc__)) static char * nsm_make_temp_pathname(const char *pathname) { @@ -421,7 +421,7 @@ nsm_drop_privileges(const int pidfd) */ if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) { xlog(L_ERROR, "prctl(PR_SET_KEEPCAPS) failed: %m"); - return 0; + return false; } if (setgroups(0, NULL) == -1) { @@ -568,9 +568,8 @@ nsm_retire_monitored_hosts(void) while ((de = readdir(dir)) != NULL) { char *src, *dst; + struct stat stb; - if (de->d_type != (unsigned char)DT_REG) - continue; if (de->d_name[0] == '.') continue; @@ -580,6 +579,20 @@ nsm_retire_monitored_hosts(void) continue; } + /* NB: not all file systems fill in d_type correctly */ + if (lstat(src, &stb) == -1) { + xlog_warn("Bad monitor file %s, skipping: %m", + de->d_name); + free(src); + continue; + } + if (!S_ISREG(stb.st_mode)) { + xlog(D_GENERAL, "Skipping non-regular file %s", + de->d_name); + free(src); + continue; + } + dst = nsm_make_record_pathname(NSM_NOTIFY_DIR, de->d_name); if (dst == NULL) { free(src); @@ -634,7 +647,7 @@ nsm_priv_to_hex(const char *priv, char *buf, const size_t buflen) /* * Returns the length in bytes of the created record. */ -__attribute_noinline__ +__attribute__((__noinline__)) static size_t nsm_create_monitor_record(char *buf, const size_t buflen, const struct sockaddr *sap, const struct mon *m) @@ -784,7 +797,7 @@ out: return result; } -__attribute_noinline__ +__attribute__((__noinline__)) static _Bool nsm_parse_line(char *line, struct sockaddr_in *sin, struct mon *m) { @@ -846,7 +859,7 @@ nsm_read_line(const char *hostname, const time_t timestamp, char *line, } /* - * Given a filename, reads data from a file under NSM_MONITOR_DIR + * Given a filename, reads data from a file under "directory" * and invokes @func so caller can populate their in-core * database with this data. */ @@ -863,10 +876,15 @@ nsm_load_host(const char *directory, const char *filename, nsm_populate_t func) if (path == NULL) goto out_err; - if (stat(path, &stb) == -1) { + if (lstat(path, &stb) == -1) { xlog(L_ERROR, "Failed to stat %s: %m", path); goto out_freepath; } + if (!S_ISREG(stb.st_mode)) { + xlog(D_GENERAL, "Skipping non-regular file %s", + path); + goto out_freepath; + } f = fopen(path, "r"); if (f == NULL) { @@ -913,8 +931,6 @@ nsm_load_dir(const char *directory, nsm_populate_t func) } while ((de = readdir(dir)) != NULL) { - if (de->d_type != (unsigned char)DT_REG) - continue; if (de->d_name[0] == '.') continue; diff --git a/tests/nsm_client/nsm_client.c b/tests/nsm_client/nsm_client.c index 0d1159a..0fa3422 100644 --- a/tests/nsm_client/nsm_client.c +++ b/tests/nsm_client/nsm_client.c @@ -205,7 +205,7 @@ nsm_client_get_rpcclient(const char *node) { unsigned short port; struct addrinfo *ai; - struct addrinfo hints = { .ai_flags = AI_ADDRCONFIG }; + struct addrinfo hints = { }; int err; CLIENT *client = NULL; diff --git a/utils/Makefile.am b/utils/Makefile.am index 8665183..a0ea116 100644 --- a/utils/Makefile.am +++ b/utils/Makefile.am @@ -4,6 +4,9 @@ OPTDIRS = if CONFIG_NFSV4 OPTDIRS += idmapd +if CONFIG_NFSIDMAP +OPTDIRS += nfsidmap +endif endif if CONFIG_GSS diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c index b78957f..b107c7c 100644 --- a/utils/exportfs/exportfs.c +++ b/utils/exportfs/exportfs.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "sockaddr.h" #include "misc.h" @@ -41,6 +42,7 @@ static void error(nfs_export *exp, int err); static void usage(const char *progname); static void validate_export(nfs_export *exp); static int matchhostname(const char *hostname1, const char *hostname2); +static void export_d_read(const char *dname); int main(int argc, char **argv) @@ -127,8 +129,10 @@ main(int argc, char **argv) return 0; } } - if (f_export && ! f_ignore) + if (f_export && ! f_ignore) { export_read(_PATH_EXPORTS); + export_d_read(_PATH_EXPORTS_D); + } if (f_export) { if (f_all) export_all(f_verbose); @@ -246,7 +250,7 @@ static void exportfs(char *arg, char *options, int verbose) { struct exportent *eep; - nfs_export *exp; + nfs_export *exp = NULL; struct addrinfo *ai = NULL; char *path; char *hname = arg; @@ -485,6 +489,59 @@ out: return result; } +/* Based on mnt_table_parse_dir() in + util-linux-ng/shlibs/mount/src/tab_parse.c */ +static void +export_d_read(const char *dname) +{ + int n = 0, i; + struct dirent **namelist = NULL; + + + n = scandir(dname, &namelist, NULL, versionsort); + if (n < 0) + xlog(L_NOTICE, "scandir %s: %s\n", dname, strerror(errno)); + else if (n == 0) + return; + + for (i = 0; i < n; i++) { + struct dirent *d = namelist[i]; + size_t namesz; + char fname[PATH_MAX + 1]; + int fname_len; + + + if (d->d_type != DT_UNKNOWN + && d->d_type != DT_REG + && d->d_type != DT_LNK) + continue; + if (*d->d_name == '.') + continue; + +#define _EXT_EXPORT_SIZ (sizeof(_EXT_EXPORT) - 1) + namesz = strlen(d->d_name); + if (!namesz + || namesz < _EXT_EXPORT_SIZ + 1 + || strcmp(d->d_name + (namesz - _EXT_EXPORT_SIZ), + _EXT_EXPORT)) + continue; + + fname_len = snprintf(fname, PATH_MAX +1, "%s/%s", dname, d->d_name); + if (fname_len > PATH_MAX) { + xlog(L_WARNING, "Too long file name: %s in %s\n", d->d_name, dname); + continue; + } + + export_read(fname); + } + + for (i = 0; i < n; i++) + free(namelist[i]); + free(namelist); + + return; +} + static char dumpopt(char c, char *fmt, ...) { diff --git a/utils/exportfs/exportfs.man b/utils/exportfs/exportfs.man index 089f75b..364f247 100644 --- a/utils/exportfs/exportfs.man +++ b/utils/exportfs/exportfs.man @@ -37,11 +37,15 @@ when a client sends an NFS MOUNT request. .PP Normally the master export table is initialized with the contents of .I /etc/exports +and files under +.I /etc/exports.d by invoking .BR "exportfs -a" . However, a system administrator can choose to add or delete exports without modifying .I /etc/exports +or files under +.I /etc/exports.d by using the .B exportfs command. @@ -92,17 +96,24 @@ Specify a list of export options in the same manner as in .B -i Ignore the .I /etc/exports -file. Only default options and options given on the command line are used. +file and files under +.I /etc/exports.d +directory. Only default options and options given on the command line are used. .TP .B -r Reexport all directories, synchronizing .I /var/lib/nfs/etab with -.IR /etc/exports . +.IR /etc/exports +and files under +.IR /etc/exports.d . This option removes entries in .I /var/lib/nfs/etab which have been deleted from -.I /etc/exports, and removes any entries from the +.I /etc/exports +or files under +.IR /etc/exports.d , +and removes any entries from the kernel export table which are no longer valid. .TP .B -u @@ -130,6 +141,8 @@ when adding new entries to the export table. When using .BR "exportfs -a" , all exports listed in .I /etc/exports +and files under +.I /etc/exports.d are added to .IR /var/lib/nfs/etab . The kernel's export table is also updated as needed. @@ -149,7 +162,9 @@ several sources. The default export options are .BR sync,ro,root_squash,wdelay . These can be overridden by entries in -.IR /etc/exports . +.IR /etc/exports +or files under +.IR /etc/exports.d . .PP A system administrator may override options from these sources using the .B -o @@ -188,6 +203,8 @@ to display the export options for each export. .SH EXAMPLES The following adds all directories listed in .I /etc/exports +and files under +.I /etc/exports.d to .I /var/lib/nfs/etab and pushes the resulting export entries into the kernel: @@ -215,7 +232,9 @@ directory: .fi .PP To unexport all exports listed in -.IR /etc/exports : +.IR /etc/exports +and files under +.IR /etc/exports.d : .PP .nf .B "# exportfs -au @@ -238,6 +257,13 @@ if they themselves are no longer valid they will be removed. .I /etc/exports input file listing exports, export options, and access control lists .TP 2.5i +.I /etc/exports.d +directory where extra input files are stored. +.B Note: +only files that end with +.I .exports +are used. +.TP 2.5i .I /var/lib/nfs/etab master table of exports .TP 2.5i diff --git a/utils/exportfs/exports.man b/utils/exportfs/exports.man index c726dd9..85e25d4 100644 --- a/utils/exportfs/exports.man +++ b/utils/exportfs/exports.man @@ -145,7 +145,9 @@ storage (see .IR async above). -In releases of nfs-utils up to and including 1.0.0, this option was the +In releases of nfs-utils up to and including 1.0.0, the +.I async +option was the default. In all releases after 1.0.0, .I sync is the default, and @@ -375,20 +377,6 @@ If the client asks for alternative locations for the export point, it will be given this list of alternatives. (Note that actual replication of the filesystem must be handled elsewhere.) -.TP -.IR refer= path@host[+host][:path@host[+host]] -A client referencing the export point will be directed to choose from -the given list an alternative location for the filesystem. -(Note that the server must have a mountpoint here, though a different -filesystem is not required; so, for example, -.IR "mount --bind" " /path /path" -is sufficient.) -.TP -.IR replicas= path@host[+host][:path@host[+host]] -If the client asks for alternative locations for the export point, it -will be given this list of alternatives. (Note that actual replication -of the filesystem must be handled elsewhere.) - .SS User ID Mapping .PP .B nfsd @@ -456,6 +444,24 @@ export entry for .B /home/joe in the example section below, which maps all requests to uid 150 (which is supposedly that of user joe). +.SS Extra Export Tables +After reading +.I /etc/exports +.B exportfs +reads files under +.I /etc/exports.d. +directory as extra export tables. +.B exportfs +regards only a file which name is ended with +.I .exports +and +not started with +.I . +as an extra export file. A file which name +is not met this condition is just ignored. +The format for extra export tables is the same as +.I /etc/exports +. .IP .SH EXAMPLE .PP @@ -501,6 +507,7 @@ all three mounts with the `sync' option enabled. '''entry. .SH FILES /etc/exports +/etc/exports.d .SH SEE ALSO .BR exportfs (8), .BR netgroup (5), diff --git a/utils/gssd/Makefile.am b/utils/gssd/Makefile.am index 95a2bd0..d7888ad 100644 --- a/utils/gssd/Makefile.am +++ b/utils/gssd/Makefile.am @@ -51,7 +51,9 @@ svcgssd_SOURCES = \ svcgssd_main_loop.c \ svcgssd_mech2file.c \ svcgssd_proc.c \ + svcgssd_krb5.c \ \ + svcgssd_krb5.h \ svcgssd.h svcgssd_LDADD = \ diff --git a/utils/gssd/gss_util.c b/utils/gssd/gss_util.c index 8fe1e9b..ca27d61 100644 --- a/utils/gssd/gss_util.c +++ b/utils/gssd/gss_util.c @@ -138,6 +138,83 @@ display_status_1(char *m, u_int32_t code, int type, const gss_OID mech) } } #endif +static char * +gss_display_error(OM_uint32 status) +{ + char *error = NULL; + + switch(status) { + case GSS_S_COMPLETE: + error = "GSS_S_COMPLETE"; + break; + case GSS_S_CALL_INACCESSIBLE_READ: + error = "GSS_S_CALL_INACCESSIBLE_READ"; + break; + case GSS_S_CALL_INACCESSIBLE_WRITE: + error = "GSS_S_CALL_INACCESSIBLE_WRITE"; + break; + case GSS_S_CALL_BAD_STRUCTURE: + error = "GSS_S_CALL_BAD_STRUCTURE"; + break; + case GSS_S_BAD_MECH: + error = "GSS_S_BAD_MECH"; + break; + case GSS_S_BAD_NAME: + error = "GSS_S_BAD_NAME"; + break; + case GSS_S_BAD_NAMETYPE: + error = "GSS_S_BAD_NAMETYPE"; + break; + case GSS_S_BAD_BINDINGS: + error = "GSS_S_BAD_BINDINGS"; + break; + case GSS_S_BAD_STATUS: + error = "GSS_S_BAD_STATUS"; + break; + case GSS_S_BAD_SIG: + error = "GSS_S_BAD_SIG"; + break; + case GSS_S_NO_CRED: + error = "GSS_S_NO_CRED"; + break; + case GSS_S_NO_CONTEXT: + error = "GSS_S_NO_CONTEXT"; + break; + case GSS_S_DEFECTIVE_TOKEN: + error = "GSS_S_DEFECTIVE_TOKEN"; + break; + case GSS_S_DEFECTIVE_CREDENTIAL: + error = "GSS_S_DEFECTIVE_CREDENTIAL"; + break; + case GSS_S_CREDENTIALS_EXPIRED: + error = "GSS_S_CREDENTIALS_EXPIRED"; + break; + case GSS_S_CONTEXT_EXPIRED: + error = "GSS_S_CONTEXT_EXPIRED"; + break; + case GSS_S_FAILURE: + error = "GSS_S_FAILURE"; + break; + case GSS_S_BAD_QOP: + error = "GSS_S_BAD_QOP"; + break; + case GSS_S_UNAUTHORIZED: + error = "GSS_S_UNAUTHORIZED"; + break; + case GSS_S_UNAVAILABLE: + error = "GSS_S_UNAVAILABLE"; + break; + case GSS_S_DUPLICATE_ELEMENT: + error = "GSS_S_DUPLICATE_ELEMENT"; + break; + case GSS_S_NAME_NOT_MN: + error = "GSS_S_NAME_NOT_MN"; + break; + default: + error = "Not defined"; + } + return error; +} static void display_status_2(char *m, u_int32_t major, u_int32_t minor, const gss_OID mech) @@ -175,8 +252,8 @@ display_status_2(char *m, u_int32_t major, u_int32_t minor, const gss_OID mech) if (major == GSS_S_CREDENTIALS_EXPIRED) msg_verbosity = 1; - printerr(msg_verbosity, "ERROR: GSS-API: error in %s(): %s - %s\n", - m, maj, min); + printerr(msg_verbosity, "ERROR: GSS-API: error in %s(): %s (%s) - %s(%s)\n", + m, gss_display_error(major), maj, min); if (maj_gss_buf.length != 0) (void) gss_release_buffer(&min_stat1, &maj_gss_buf); @@ -199,20 +276,25 @@ gssd_acquire_cred(char *server_name, const gss_OID oid) u_int32_t ignore_maj_stat, ignore_min_stat; gss_buffer_desc pbuf; - name.value = (void *)server_name; - name.length = strlen(server_name); + /* If server_name is NULL, get cred for GSS_C_NO_NAME */ + if (server_name == NULL) { + target_name = GSS_C_NO_NAME; + } else { + name.value = (void *)server_name; + name.length = strlen(server_name); - maj_stat = gss_import_name(&min_stat, &name, - oid, - &target_name); + maj_stat = gss_import_name(&min_stat, &name, + oid, + &target_name); - if (maj_stat != GSS_S_COMPLETE) { - pgsserr("gss_import_name", maj_stat, min_stat, g_mechOid); - return (FALSE); + if (maj_stat != GSS_S_COMPLETE) { + pgsserr("gss_import_name", maj_stat, min_stat, g_mechOid); + return (FALSE); + } } - maj_stat = gss_acquire_cred(&min_stat, target_name, 0, - GSS_C_NULL_OID_SET, GSS_C_ACCEPT, + maj_stat = gss_acquire_cred(&min_stat, target_name, GSS_C_INDEFINITE, + GSS_C_NO_OID_SET, GSS_C_ACCEPT, &gssd_creds, NULL, NULL); if (maj_stat != GSS_S_COMPLETE) { diff --git a/utils/gssd/gssd.man b/utils/gssd/gssd.man index 0a23cd6..073379d 100644 --- a/utils/gssd/gssd.man +++ b/utils/gssd/gssd.man @@ -53,6 +53,8 @@ To be more consistent with other implementations, we now look for specific keytab entries. The search order for keytabs to be used for "machine credentials" is now: .br + $@ +.br root/@ .br nfs/@ @@ -64,6 +66,9 @@ for "machine credentials" is now: nfs/@ .br host/@ +.IP +If this search order does not use the correct key then provide a +keytab file that contains only correct keys. .TP .B -p path Tells diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c index c301d46..41328c9 100644 --- a/utils/gssd/gssd_proc.c +++ b/utils/gssd/gssd_proc.c @@ -1245,7 +1245,7 @@ handle_gssd_upcall(struct clnt_info *clp) goto out; if (sscanf(p, "enctypes=%s", enctypes) != 1) { printerr(0, "WARNING: handle_gssd_upcall: " - "failed to parse target name " + "failed to parse encryption types " "in upcall string '%s'\n", lbuf); goto out; } diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c index f071600..4b13fa1 100644 --- a/utils/gssd/krb5_util.c +++ b/utils/gssd/krb5_util.c @@ -768,6 +768,7 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname, krb5_error_code code; char **realmnames = NULL; char myhostname[NI_MAXHOST], targethostname[NI_MAXHOST]; + char myhostad[NI_MAXHOST+1]; int i, j, retval; char *default_realm = NULL; char *realm; @@ -789,6 +790,14 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname, printerr(1, "%s while getting local hostname\n", k5err); goto out; } + + /* Compute the active directory machine name HOST$ */ + strcpy(myhostad, myhostname); + for (i = 0; myhostad[i] != 0; ++i) + myhostad[i] = toupper(myhostad[i]); + myhostad[i] = '$'; + myhostad[i+1] = 0; + retval = get_full_hostname(myhostname, myhostname, sizeof(myhostname)); if (retval) goto out; @@ -833,32 +842,47 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname, if (strcmp(realm, default_realm) == 0) tried_default = 1; for (j = 0; svcnames[j] != NULL; j++) { - code = krb5_build_principal_ext(context, &princ, - strlen(realm), - realm, - strlen(svcnames[j]), - svcnames[j], - strlen(myhostname), - myhostname, - NULL); + char spn[300]; + + /* + * The special svcname "$" means 'try the active + * directory machine account' + */ + if (strcmp(svcnames[j],"$") == 0) { + snprintf(spn, sizeof(spn), "%s@%s", myhostad, realm); + code = krb5_build_principal_ext(context, &princ, + strlen(realm), + realm, + strlen(myhostad), + myhostad, + NULL); + } else { + snprintf(spn, sizeof(spn), "%s/%s@%s", + svcnames[j], myhostname, realm); + code = krb5_build_principal_ext(context, &princ, + strlen(realm), + realm, + strlen(svcnames[j]), + svcnames[j], + strlen(myhostname), + myhostname, + NULL); + } + if (code) { k5err = gssd_k5_err_msg(context, code); - printerr(1, "%s while building principal for " - "'%s/%s@%s'\n", k5err, svcnames[j], - myhostname, realm); + printerr(1, "%s while building principal for '%s'\n", + k5err, spn); continue; } code = krb5_kt_get_entry(context, kt, princ, 0, 0, kte); krb5_free_principal(context, princ); if (code) { k5err = gssd_k5_err_msg(context, code); - printerr(3, "%s while getting keytab entry for " - "'%s/%s@%s'\n", k5err, svcnames[j], - myhostname, realm); + printerr(3, "%s while getting keytab entry for '%s'\n", + k5err, spn); } else { - printerr(3, "Success getting keytab entry for " - "'%s/%s@%s'\n", - svcnames[j], myhostname, realm); + printerr(3, "Success getting keytab entry for '%s'\n",spn); retval = 0; goto out; } @@ -870,6 +894,8 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname, */ for (j = 0; svcnames[j] != NULL; j++) { int found = 0; + if (strcmp(svcnames[j],"$") == 0) + continue; code = gssd_search_krb5_keytab(context, kt, realm, svcnames[j], &found, kte); if (!code && found) { @@ -1160,7 +1186,7 @@ gssd_refresh_krb5_machine_credential(char *hostname, krb5_keytab kt = NULL;; int retval = 0; char *k5err = NULL; - const char *svcnames[4] = { "root", "nfs", "host", NULL }; + const char *svcnames[5] = { "$", "root", "nfs", "host", NULL }; /* * If a specific service name was specified, use it. diff --git a/utils/gssd/svcgssd.c b/utils/gssd/svcgssd.c index 9b463f3..1afff9e 100644 --- a/utils/gssd/svcgssd.c +++ b/utils/gssd/svcgssd.c @@ -262,11 +262,19 @@ main(int argc, char *argv[]) "/etc/krb5.keytab?\n"); exit(1); } + } else { + status = gssd_acquire_cred(NULL, + (const gss_OID)GSS_C_NT_HOSTBASED_SERVICE); + if (status == FALSE) { + printerr(0, "unable to obtain nameless credentials\n"); + exit(1); + } } if (!fg) release_parent(); + nfs4_init_name_mapping(NULL); /* XXX: should only do this once */ gssd_run(); printerr(0, "gssd_run returned!\n"); abort(); diff --git a/utils/gssd/svcgssd_krb5.c b/utils/gssd/svcgssd_krb5.c new file mode 100644 index 0000000..fc67a6f --- /dev/null +++ b/utils/gssd/svcgssd_krb5.c @@ -0,0 +1,200 @@ +/* + * COPYRIGHT (c) 2011 + * The Regents of the University of Michigan + * ALL RIGHTS RESERVED + * + * Permission is granted to use, copy, create derivative works + * and redistribute this software and such derivative works + * for any purpose, so long as the name of The University of + * Michigan is not used in any advertising or publicity + * pertaining to the use of distribution of this software + * without specific, written prior authorization. If the + * above copyright notice or any other identification of the + * University of Michigan is included in any copy of any + * portion of this software, then the disclaimer below must + * also be included. + * + * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION + * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY + * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF + * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING + * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE + * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE + * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR + * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING + * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN + * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGES. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include + +#include "gss_util.h" +#include "gss_oids.h" +#include "err_util.h" +#include "svcgssd_krb5.h" + +#define MYBUFLEN 1024 + +char *supported_enctypes_filename = "/proc/fs/nfsd/supported_krb5_enctypes"; +int parsed_num_enctypes = 0; +krb5_enctype *parsed_enctypes = NULL; +char *cached_enctypes = NULL; + +/*==========================*/ +/*=== Internal routines ===*/ +/*==========================*/ + +/* + * Parse the supported encryption type information + */ +static int +parse_enctypes(char *enctypes) +{ + int n = 0; + char *curr, *comma; + int i; + + /* Don't parse the same string over and over... */ + if (cached_enctypes && strcmp(cached_enctypes, enctypes) == 0) + return 0; + + /* Free any existing cached_enctypes */ + free(cached_enctypes); + + if (parsed_enctypes != NULL) { + free(parsed_enctypes); + parsed_enctypes = NULL; + parsed_num_enctypes = 0; + } + + /* count the number of commas */ + for (curr = enctypes; curr && *curr != '\0'; curr = ++comma) { + comma = strchr(curr, ','); + if (comma != NULL) + n++; + else + break; + } + + /* If no more commas and we're not at the end, there's one more value */ + if (*curr != '\0') + n++; + + /* Empty string, return an error */ + if (n == 0) + return ENOENT; + + /* Allocate space for enctypes array */ + if ((parsed_enctypes = (int *) calloc(n, sizeof(int))) == NULL) { + return ENOMEM; + } + + /* Now parse each value into the array */ + for (curr = enctypes, i = 0; curr && *curr != '\0'; curr = ++comma) { + parsed_enctypes[i++] = atoi(curr); + comma = strchr(curr, ','); + if (comma == NULL) + break; + } + + parsed_num_enctypes = n; + if ((cached_enctypes = malloc(strlen(enctypes)+1))) + strcpy(cached_enctypes, enctypes); + + return 0; +} + +static void +get_kernel_supported_enctypes(void) +{ + FILE *s_e; + int ret; + char buffer[MYBUFLEN + 1]; + + memset(buffer, '\0', sizeof(buffer)); + + s_e = fopen(supported_enctypes_filename, "r"); + if (s_e == NULL) + goto out_clean_parsed; + + ret = fread(buffer, 1, MYBUFLEN, s_e); + if (ret < 0) { + fclose(s_e); + goto out_clean_parsed; + } + fclose(s_e); + if (parse_enctypes(buffer)) { + goto out_clean_parsed; + } +out: + return; + +out_clean_parsed: + if (parsed_enctypes != NULL) { + free(parsed_enctypes); + parsed_num_enctypes = 0; + } + goto out; +} + +/*==========================*/ +/*=== External routines ===*/ +/*==========================*/ + +/* + * Get encryption types supported by the kernel, and then + * call gss_krb5_set_allowable_enctypes() to limit the + * encryption types negotiated. + * + * Returns: + * 0 => all went well + * -1 => there was an error + */ + +int +svcgssd_limit_krb5_enctypes(void) +{ +#ifdef HAVE_SET_ALLOWABLE_ENCTYPES + u_int maj_stat, min_stat; + krb5_enctype default_enctypes[] = { ENCTYPE_DES_CBC_CRC, + ENCTYPE_DES_CBC_MD5, + ENCTYPE_DES_CBC_MD4 }; + int default_num_enctypes = + sizeof(default_enctypes) / sizeof(default_enctypes[0]); + krb5_enctype *enctypes; + int num_enctypes; + + get_kernel_supported_enctypes(); + + if (parsed_enctypes != NULL) { + enctypes = parsed_enctypes; + num_enctypes = parsed_num_enctypes; + } else { + enctypes = default_enctypes; + num_enctypes = default_num_enctypes; + } + + maj_stat = gss_set_allowable_enctypes(&min_stat, gssd_creds, + &krb5oid, num_enctypes, enctypes); + if (maj_stat != GSS_S_COMPLETE) { + printerr(1, "WARNING: gss_set_allowable_enctypes failed\n"); + pgsserr("svcgssd_limit_krb5_enctypes: gss_set_allowable_enctypes", + maj_stat, min_stat, &krb5oid); + return -1; + } +#endif + return 0; +} diff --git a/utils/gssd/svcgssd_krb5.h b/utils/gssd/svcgssd_krb5.h new file mode 100644 index 0000000..07d5eb9 --- /dev/null +++ b/utils/gssd/svcgssd_krb5.h @@ -0,0 +1,36 @@ +/* + * COPYRIGHT (c) 2011 + * The Regents of the University of Michigan + * ALL RIGHTS RESERVED + * + * Permission is granted to use, copy, create derivative works + * and redistribute this software and such derivative works + * for any purpose, so long as the name of The University of + * Michigan is not used in any advertising or publicity + * pertaining to the use of distribution of this software + * without specific, written prior authorization. If the + * above copyright notice or any other identification of the + * University of Michigan is included in any copy of any + * portion of this software, then the disclaimer below must + * also be included. + * + * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION + * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY + * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF + * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING + * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE + * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE + * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR + * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING + * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN + * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGES. + */ + +#ifndef SVCGSSD_KRB5_H +#define SVCGSSD_KRB5_H + +int svcgssd_limit_krb5_enctypes(void); + +#endif /* SVCGSSD_KRB5_H */ diff --git a/utils/gssd/svcgssd_proc.c b/utils/gssd/svcgssd_proc.c index 3894078..7a916d7 100644 --- a/utils/gssd/svcgssd_proc.c +++ b/utils/gssd/svcgssd_proc.c @@ -57,6 +57,7 @@ #include "err_util.h" #include "context.h" #include "gss_oids.h" +#include "svcgssd_krb5.h" extern char * mech2file(gss_OID mech); #define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.rpcsec.context/channel" @@ -241,7 +242,7 @@ get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred) "file for name '%s'\n", sname); goto out_free; } - nfs4_init_name_mapping(NULL); /* XXX: should only do this once */ + res = nfs4_gss_princ_to_ids(secname, sname, &uid, &gid); if (res < 0) { /* @@ -443,6 +444,10 @@ handle_nullreq(FILE *f) { memcpy(&ctx, in_handle.value, in_handle.length); } + if (svcgssd_limit_krb5_enctypes()) { + goto out_err; + } + maj_stat = gss_accept_sec_context(&min_stat, &ctx, gssd_creds, &in_tok, GSS_C_NO_CHANNEL_BINDINGS, &client_name, &mech, &out_tok, &ret_flags, NULL, NULL); diff --git a/utils/idmapd/Makefile.am b/utils/idmapd/Makefile.am index 4218048..4328e41 100644 --- a/utils/idmapd/Makefile.am +++ b/utils/idmapd/Makefile.am @@ -11,12 +11,8 @@ EXTRA_DIST = \ idmapd.conf idmapd_SOURCES = \ - atomicio.c \ idmapd.c \ - strlcat.c \ - strlcpy.c \ \ - cfg.h \ nfs_idmap.h \ queue.h diff --git a/utils/idmapd/atomicio.c b/utils/idmapd/atomicio.c deleted file mode 100644 index 1fb1ff9..0000000 --- a/utils/idmapd/atomicio.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2002 Marius Aamodt Eriksen - * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved. - * 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. - */ - -#include -#include -#include - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -/* - * ensure all of data on socket comes through. f==read || f==write - */ -ssize_t -atomicio( - ssize_t (*f) (int, void*, size_t), - int fd, - void *_s, - size_t n) -{ - char *s = _s; - ssize_t res; - size_t pos = 0; - - while (n > pos) { - res = (f) (fd, s + pos, n - pos); - switch (res) { - case -1: - if (errno == EINTR || errno == EAGAIN) - continue; - case 0: - if (pos != 0) - return (pos); - return (res); - default: - pos += res; - } - } - return (pos); -} diff --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c index b76607a..76a56ef 100644 --- a/utils/idmapd/idmapd.c +++ b/utils/idmapd/idmapd.c @@ -158,10 +158,6 @@ 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); -size_t strlcpy(char *, const char *, size_t); -ssize_t atomicio(ssize_t (*f) (int, void*, size_t), - int, void *, size_t); void mydaemon(int, int); void release_parent(void); diff --git a/utils/idmapd/strlcat.c b/utils/idmapd/strlcat.c deleted file mode 100644 index daedd7a..0000000 --- a/utils/idmapd/strlcat.c +++ /dev/null @@ -1,76 +0,0 @@ -/* $OpenBSD: strlcat.c,v 1.8 2001/05/13 15:40:15 deraadt Exp $ */ - -/* - * Copyright (c) 1998 Todd C. Miller - * 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. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``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. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char *rcsid = "$OpenBSD: strlcat.c,v 1.8 2001/05/13 15:40:15 deraadt Exp $"; -#endif /* LIBC_SCCS and not lint */ - -#include -#include - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -/* - * Appends src to string dst of size siz (unlike strncat, siz is the - * full size of dst, not space left). At most siz-1 characters - * will be copied. Always NUL terminates (unless siz <= strlen(dst)). - * Returns strlen(src) + MIN(siz, strlen(initial dst)). - * If retval >= siz, truncation occurred. - */ -size_t -strlcat(char *dst, - const char *src, - size_t siz) -{ - register char *d = dst; - register const char *s = src; - register size_t n = siz; - size_t dlen; - - /* Find the end of dst and adjust bytes left but don't go past end */ - while (n-- != 0 && *d != '\0') - d++; - dlen = d - dst; - n = siz - dlen; - - if (n == 0) - return(dlen + strlen(s)); - while (*s != '\0') { - if (n != 1) { - *d++ = *s; - n--; - } - s++; - } - *d = '\0'; - - return(dlen + (s - src)); /* count does not include NUL */ -} diff --git a/utils/idmapd/strlcpy.c b/utils/idmapd/strlcpy.c deleted file mode 100644 index a2653ee..0000000 --- a/utils/idmapd/strlcpy.c +++ /dev/null @@ -1,72 +0,0 @@ -/* $OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $ */ - -/* - * Copyright (c) 1998 Todd C. Miller - * 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. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``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. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char *rcsid = "$OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $"; -#endif /* LIBC_SCCS and not lint */ - -#include -#include - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -/* - * Copy src to string dst of size siz. At most siz-1 characters - * will be copied. Always NUL terminates (unless siz == 0). - * Returns strlen(src); if retval >= siz, truncation occurred. - */ -size_t -strlcpy(char *dst, - const char *src, - size_t siz) -{ - register char *d = dst; - register const char *s = src; - register size_t n = siz; - - /* Copy as many bytes as will fit */ - if (n != 0 && --n != 0) { - do { - if ((*d++ = *s++) == 0) - break; - } while (--n != 0); - } - - /* Not enough room in dst, add NUL and traverse rest of src */ - if (n == 0) { - if (siz != 0) - *d = '\0'; /* NUL-terminate dst */ - while (*s++) - ; - } - - return(s - src - 1); /* count does not include NUL */ -} diff --git a/utils/mount/Makefile.am b/utils/mount/Makefile.am index 299384a..056293c 100644 --- a/utils/mount/Makefile.am +++ b/utils/mount/Makefile.am @@ -9,17 +9,17 @@ man5_MANS = nfs.man sbin_PROGRAMS = mount.nfs EXTRA_DIST = nfsmount.x $(man8_MANS) $(man5_MANS) -mount_nfs_SOURCES = mount.c error.c network.c fstab.c token.c \ +mount_common = error.c network.c fstab.c token.c \ parse_opt.c parse_dev.c \ nfsmount.c nfs4mount.c stropts.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 \ - mount_config.h + mount_config.h utils.c utils.h if MOUNT_CONFIG -mount_nfs_SOURCES += configfile.c +mount_common += configfile.c man5_MANS += nfsmount.conf.man EXTRA_DIST += nfsmount.conf endif @@ -27,6 +27,15 @@ endif mount_nfs_LDADD = ../../support/nfs/libnfs.a \ ../../support/export/libexport.a +mount_nfs_SOURCES = $(mount_common) + +if CONFIG_LIBMOUNT +mount_nfs_SOURCES += mount_libmount.c +mount_nfs_LDADD += $(LIBMOUNT) +else +mount_nfs_SOURCES += mount.c +endif + MAINTAINERCLEANFILES = Makefile.in install-exec-hook: diff --git a/utils/mount/fstab.c b/utils/mount/fstab.c index 051fa38..a742e64 100644 --- a/utils/mount/fstab.c +++ b/utils/mount/fstab.c @@ -364,19 +364,22 @@ lock_mtab (void) { /* Repeat until it was us who made the link */ while (!we_created_lockfile) { struct flock flock; - int errsv, j; + int j; j = link(linktargetfile, MOUNTED_LOCK); - errsv = errno; - if (j == 0) - we_created_lockfile = 1; + { + int errsv = errno; - if (j < 0 && errsv != EEXIST) { - (void) unlink(linktargetfile); - die (EX_FILEIO, _("can't link lock file %s: %s " - "(use -n flag to override)"), - MOUNTED_LOCK, strerror (errsv)); + if (j == 0) + we_created_lockfile = 1; + + if (j < 0 && errsv != EEXIST) { + (void) unlink(linktargetfile); + die (EX_FILEIO, _("can't link lock file %s: %s " + "(use -n flag to override)"), + MOUNTED_LOCK, strerror (errsv)); + } } lockfile_fd = open (MOUNTED_LOCK, O_WRONLY); @@ -414,7 +417,7 @@ lock_mtab (void) { } (void) unlink(linktargetfile); } else { - static int tries = 0; + static int retries = 0; /* Someone else made the link. Wait. */ alarm(LOCK_TIMEOUT); @@ -428,10 +431,10 @@ lock_mtab (void) { alarm(0); /* Limit the number of iterations - maybe there still is some old /etc/mtab~ */ - ++tries; - if (tries % 200 == 0) + ++retries; + if (retries % 200 == 0) usleep(30); - if (tries > 100000) { + if (retries > 100000) { (void) unlink(linktargetfile); close(lockfile_fd); die (EX_FILEIO, _("Cannot create link %s\n" diff --git a/utils/mount/mount.c b/utils/mount/mount.c index 82b9169..f3f0a83 100644 --- a/utils/mount/mount.c +++ b/utils/mount/mount.c @@ -47,7 +47,7 @@ #include "mount.h" #include "error.h" #include "stropts.h" -#include "version.h" +#include "utils.h" char *progname; int nfs_mount_data_version; @@ -150,49 +150,6 @@ static const struct opt_map opt_map[] = { static void parse_opts(const char *options, int *flags, char **extra_opts); /* - * Choose the version of the nfs_mount_data structure that is appropriate - * for the kernel that is doing the mount. - * - * NFS_MOUNT_VERSION: maximum version supported by these sources - * nfs_mount_data_version: maximum version supported by the running kernel - */ -static void discover_nfs_mount_data_version(void) -{ - unsigned int kernel_version = linux_version_code(); - - if (kernel_version) { - if (kernel_version < MAKE_VERSION(2, 1, 32)) - nfs_mount_data_version = 1; - else if (kernel_version < MAKE_VERSION(2, 2, 18)) - nfs_mount_data_version = 3; - else if (kernel_version < MAKE_VERSION(2, 3, 0)) - nfs_mount_data_version = 4; - else if (kernel_version < MAKE_VERSION(2, 3, 99)) - nfs_mount_data_version = 3; - else if (kernel_version < MAKE_VERSION(2, 6, 3)) - nfs_mount_data_version = 4; - else - nfs_mount_data_version = 6; - } - if (nfs_mount_data_version > NFS_MOUNT_VERSION) - nfs_mount_data_version = NFS_MOUNT_VERSION; - else - if (kernel_version > MAKE_VERSION(2, 6, 22)) - string++; -} - -static void print_one(char *spec, char *node, char *type, char *opts) -{ - if (!verbose) - return; - - if (opts) - printf(_("%s on %s type %s (%s)\n"), spec, node, type, opts); - else - printf(_("%s on %s type %s\n"), spec, node, type); -} - -/* * Build a canonical mount option string for /etc/mtab. */ static char *fix_opts_string(int flags, const char *extra_opts) @@ -209,7 +166,7 @@ static char *fix_opts_string(int flags, const char *extra_opts) } if (flags & MS_USERS) new_opts = xstrconcat3(new_opts, ",users", ""); - + for (om = opt_map; om->opt != NULL; om++) { if (om->skip) continue; @@ -224,6 +181,20 @@ static char *fix_opts_string(int flags, const char *extra_opts) return new_opts; } +static void +init_mntent(struct mntent *mnt, char *fsname, char *dir, char *type, + int flags, char *opts) +{ + mnt->mnt_fsname = fsname; + mnt->mnt_dir = dir; + mnt->mnt_type = type; + mnt->mnt_opts = fix_opts_string(flags & ~MS_NOMTAB, opts); + + /* these are always zero for NFS */ + mnt->mnt_freq = 0; + mnt->mnt_passno = 0; +} + /* Create mtab with a root entry. */ static void create_mtab (void) { @@ -245,11 +216,8 @@ create_mtab (void) { if ((fstab = getfsfile ("/")) || (fstab = getfsfile ("root"))) { char *extra_opts; parse_opts (fstab->m.mnt_opts, &flags, &extra_opts); - mnt.mnt_dir = "/"; - mnt.mnt_fsname = xstrdup(fstab->m.mnt_fsname); - mnt.mnt_type = fstab->m.mnt_type; - mnt.mnt_opts = fix_opts_string (flags, extra_opts); - mnt.mnt_freq = mnt.mnt_passno = 0; + init_mntent(&mnt, xstrdup(fstab->m.mnt_fsname), "/", + fstab->m.mnt_type, flags, extra_opts); free(extra_opts); if (nfs_addmntent (mfp, &mnt) == 1) { @@ -273,17 +241,12 @@ create_mtab (void) { } static int add_mtab(char *spec, char *mount_point, char *fstype, - int flags, char *opts, int freq, int pass) + int flags, char *opts) { struct mntent ment; int result = EX_SUCCESS; - ment.mnt_fsname = spec; - ment.mnt_dir = mount_point; - ment.mnt_type = fstype; - ment.mnt_opts = fix_opts_string(flags, opts); - ment.mnt_freq = freq; - ment.mnt_passno = pass; + init_mntent(&ment, spec, mount_point, fstype, flags, opts); if (!nomtab && mtab_does_not_exist()) { if (verbose > 1) @@ -321,23 +284,7 @@ static int add_mtab(char *spec, char *mount_point, char *fstype, return result; } -void mount_usage(void) -{ - printf(_("usage: %s remotetarget dir [-rvVwfnsih] [-o nfsoptions]\n"), - progname); - printf(_("options:\n")); - printf(_("\t-r\t\tMount file system readonly\n")); - printf(_("\t-v\t\tVerbose\n")); - printf(_("\t-V\t\tPrint version\n")); - printf(_("\t-w\t\tMount file system read-write\n")); - printf(_("\t-f\t\tFake mount, do not actually mount\n")); - printf(_("\t-n\t\tDo not update /etc/mtab\n")); - printf(_("\t-s\t\tTolerate sloppy mount options rather than fail\n")); - printf(_("\t-h\t\tPrint this help\n")); - printf(_("\tnfsoptions\tRefer to mount.nfs(8) or nfs(5)\n\n")); -} - -static void parse_opt(const char *opt, int *mask, char *extra_opts, int len) +static void parse_opt(const char *opt, int *mask, char *extra_opts, size_t len) { const struct opt_map *om; @@ -371,7 +318,7 @@ static void parse_opts(const char *options, int *flags, char **extra_opts) if (options != NULL) { char *opts = xstrdup(options); char *opt, *p; - int len = strlen(opts) + 1; /* include room for a null */ + size_t len = strlen(opts) + 1; /* include room for a null */ int open_quote = 0; *extra_opts = xmalloc(len); @@ -397,26 +344,6 @@ static void parse_opts(const char *options, int *flags, char **extra_opts) } } -static int chk_mountpoint(char *mount_point) -{ - struct stat sb; - - if (stat(mount_point, &sb) < 0){ - mount_error(NULL, mount_point, errno); - return 1; - } - if (S_ISDIR(sb.st_mode) == 0){ - mount_error(NULL, mount_point, ENOTDIR); - return 1; - } - if (access(mount_point, X_OK) < 0) { - mount_error(NULL, mount_point, errno); - return 1; - } - - return 0; -} - static int try_mount(char *spec, char *mount_point, int flags, char *fs_type, char **extra_opts, char *mount_opts, int fake, int bg) @@ -441,9 +368,7 @@ static int try_mount(char *spec, char *mount_point, int flags, if (!fake) print_one(spec, mount_point, fs_type, mount_opts); - ret = add_mtab(spec, mount_point, fs_type, flags, *extra_opts, - 0, 0 /* these are always zero for NFS */ ); - return ret; + return add_mtab(spec, mount_point, fs_type, flags, *extra_opts); } int main(int argc, char *argv[]) @@ -455,7 +380,7 @@ int main(int argc, char *argv[]) progname = basename(argv[0]); - discover_nfs_mount_data_version(); + nfs_mount_data_version = discover_nfs_mount_data_version(&string); if(!strncmp(progname, "umount", strlen("umount"))) exit(nfsumount(argc, argv)); diff --git a/utils/mount/mount_config.h b/utils/mount/mount_config.h index 3023306..69ffd1e 100644 --- a/utils/mount/mount_config.h +++ b/utils/mount/mount_config.h @@ -1,7 +1,7 @@ -#ifndef _LINUX_MOUNT__CONFIG_H -#define _LINUX_MOUNT_CONFIG__H +#ifndef _LINUX_MOUNT_CONFIG_H +#define _LINUX_MOUNT_CONFIG_H /* - * mount_config.h -- mount configuration file routines + * 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 @@ -16,15 +16,13 @@ * */ -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) +static inline void mount_config_init(char *program) { xlog_open(program); /* @@ -32,19 +30,22 @@ inline void mount_config_init(char *program) */ conf_init(); } -inline char *mount_config_opts(char *spec, + +static 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) { } +static inline void mount_config_init(__attribute__ ((unused)) char *program) { } -inline char *mount_config_opts(char *spec, - char *mount_point, char *mount_opts) +static inline char *mount_config_opts(__attribute__ ((unused)) char *spec, + __attribute__ ((unused)) char *mount_point, char *mount_opts) { return mount_opts; } #endif /* MOUNT_CONFIG */ -#endif + +#endif /* _LINUX_MOUNT_CONFIG_H */ diff --git a/utils/mount/mount_constants.h b/utils/mount/mount_constants.h index cbfb099..4d050d8 100644 --- a/utils/mount/mount_constants.h +++ b/utils/mount/mount_constants.h @@ -64,4 +64,8 @@ if we have a stack or plain mount - mount atop of it, forming a stack. */ #define MS_MGC_MSK 0xffff0000 /* magic flag number mask */ #endif +/* Generic options that are prevented from appearing + * in the options field in /etc/mtab. */ +#define MS_NOMTAB (MS_REMOUNT) + #endif /* _NFS_UTILS_MOUNT_CONSTANTS_H */ diff --git a/utils/mount/mount_libmount.c b/utils/mount/mount_libmount.c new file mode 100644 index 0000000..6dd6484 --- /dev/null +++ b/utils/mount/mount_libmount.c @@ -0,0 +1,413 @@ +/* + * mount_libmount.c -- Linux NFS [u]mount based on libmount + * + * Copyright (C) 2011 Karel Zak + * + * 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. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include + +#include "nls.h" +#include "mount_config.h" + +#include "nfs_mount.h" +#include "nfs4_mount.h" +#include "stropts.h" +#include "version.h" +#include "xcommon.h" + +#include "error.h" +#include "utils.h" + +char *progname; +int nfs_mount_data_version; +int verbose; +int sloppy; +int string; +int nomtab; + +#define FOREGROUND (0) +#define BACKGROUND (1) + +/* + * Store mount options to mtab (or /dev/.mount/utab), called from mount.nfs. + * + * Note that on systems without /etc/mtab the fs-specific options are not + * managed by libmount at all. We have to use "mount attributes" that are + * private for mount. helpers. + */ +static void store_mount_options(struct libmnt_fs *fs, const char *opts) +{ + mnt_fs_set_fs_options(fs, opts); /* for mtab */ + mnt_fs_set_attributes(fs, opts); /* for non-mtab systems */ +} + +/* + * Retrieve mount options from mtab (or /dev/.mount/utab) called from umount.nfs. + * + * The result can passed to free(). + */ +char *retrieve_mount_options(struct libmnt_fs *fs) +{ + const char *opts; + + if (!fs) + return NULL; + + opts = mnt_fs_get_attributes(fs); /* /dev/.mount/utab */ + if (opts) + return strdup(opts); + + return mnt_fs_strdup_options(fs); /* /etc/mtab */ +} + +static int try_mount(struct libmnt_context *cxt, int bg) +{ + struct libmnt_fs *fs; + const char *p; + char *src = NULL, *tgt = NULL, *type = NULL, *opts = NULL; + unsigned long flags = 0; + int fake, ret = 0; + + fs = mnt_context_get_fs(cxt); + + /* libmount returns read-only pointers (const char) + * so, reallocate for nfsmount() functions. + */ + if ((p = mnt_fs_get_source(fs))) /* spec */ + src = strdup(p); + if ((p = mnt_fs_get_target(fs))) /* mountpoint */ + tgt = strdup(p); + if ((p = mnt_fs_get_fstype(fs))) /* FS type */ + type = strdup(p); + if ((p = mnt_fs_get_fs_options(fs))) /* mount options */ + opts = strdup(p); + + mnt_context_get_mflags(cxt, &flags); /* mount(2) flags */ + fake = mnt_context_is_fake(cxt); + + if (string) + ret = nfsmount_string(src, tgt, type, flags, &opts, fake, bg); + + else if (strcmp(type, "nfs4") == 0) + ret = nfs4mount(src, tgt, flags, &opts, fake, bg); + else + ret = nfsmount(src, tgt, flags, &opts, fake, bg); + + /* Store mount options if not called with mount --no-mtab */ + if (!ret && !mnt_context_is_nomtab(cxt)) + store_mount_options(fs, opts); + + free(src); + free(tgt); + free(type); + free(opts); + + return ret; +} + +/* returns: error = -1, success = 0 , unknown = 1 */ +static int is_vers4(struct libmnt_context *cxt) +{ + struct libmnt_fs *fs = mnt_context_get_fs(cxt); + struct libmnt_table *tb = NULL; + const char *src = mnt_context_get_source(cxt), + *tgt = mnt_context_get_target(cxt); + int rc = 1; + + if (!src || !tgt) + return -1; + + if (!mnt_fs_is_kernel(fs)) { + struct libmnt_table *tb = mnt_new_table_from_file("/proc/mounts"); + + if (!tb) + return -1; + fs = mnt_table_find_pair(tb, src, tgt, MNT_ITER_BACKWARD); + } + + if (fs) { + const char *type = mnt_fs_get_fstype(fs); + if (type && strcmp(type, "nfs4") == 0) + rc = 0; + } + mnt_free_table(tb); + return rc; +} + +static int umount_main(struct libmnt_context *cxt, int argc, char **argv) +{ + int rc, c; + char *spec = NULL, *opts = NULL; + + static const struct option longopts[] = { + { "force", 0, 0, 'f' }, + { "help", 0, 0, 'h' }, + { "no-mtab", 0, 0, 'n' }, + { "verbose", 0, 0, 'v' }, + { "read-only", 0, 0, 'r' }, + { "lazy", 0, 0, 'l' }, + { "types", 1, 0, 't' }, + { NULL, 0, 0, 0 } + }; + + mnt_context_init_helper(cxt, MNT_ACT_UMOUNT, 0); + + while ((c = getopt_long (argc, argv, "fvnrlh", longopts, NULL)) != -1) { + + rc = mnt_context_helper_setopt(cxt, c, optarg); + if (rc == 0) /* valid option */ + continue; + if (rc < 0) /* error (probably ENOMEM) */ + goto err; + /* rc==1 means unknow option */ + umount_usage(); + return EX_USAGE; + } + + if (optind < argc) + spec = argv[optind++]; + + if (!spec || (*spec != '/' && strchr(spec,':') == NULL)) { + nfs_error(_("%s: no mount point provided"), progname); + return EX_USAGE; + } + + if (mnt_context_set_target(cxt, spec)) + goto err; + if (mnt_context_set_fstype_pattern(cxt, "nfs,nfs4")) /* restrict filesystems */ + goto err; + + /* read mtab/fstab, evaluate permissions, etc. */ + rc = mnt_context_prepare_umount(cxt); + if (rc) { + nfs_error(_("%s: failed to prepare umount: %s\n"), + progname, strerror(-rc)); + goto err; + } + + opts = retrieve_mount_options(mnt_context_get_fs(cxt)); + + if (!mnt_context_is_lazy(cxt)) { + if (opts) { + /* we have full FS description (e.g. from mtab or /proc) */ + switch (is_vers4(cxt)) { + case 0: + /* We ignore the error from nfs_umount23. + * If the actual umount succeeds (in del_mtab), + * we don't want to signal an error, as that + * could cause /sbin/mount to retry! + */ + nfs_umount23(mnt_context_get_source(cxt), opts); + break; + case 1: /* unknown */ + break; + default: /* error */ + goto err; + } + } else + /* strange, no entry in mtab or /proc not mounted */ + nfs_umount23(spec, "tcp,v3"); + } + + rc = mnt_context_do_umount(cxt); /* call umount(2) syscall */ + mnt_context_finalize_mount(cxt); /* mtab update */ + + if (rc && !mnt_context_get_status(cxt)) { + /* mnt_context_do_umount() returns errno if umount(2) failed */ + umount_error(rc, spec); + goto err; + } + + free(opts); + return EX_SUCCESS; +err: + free(opts); + return EX_FAIL; +} + +static int mount_main(struct libmnt_context *cxt, int argc, char **argv) +{ + int rc, c; + struct libmnt_fs *fs; + char *spec = NULL, *mount_point = NULL, *opts = NULL; + + static const struct option longopts[] = { + { "fake", 0, 0, 'f' }, + { "help", 0, 0, 'h' }, + { "no-mtab", 0, 0, 'n' }, + { "read-only", 0, 0, 'r' }, + { "ro", 0, 0, 'r' }, + { "verbose", 0, 0, 'v' }, + { "version", 0, 0, 'V' }, + { "read-write", 0, 0, 'w' }, + { "rw", 0, 0, 'w' }, + { "options", 1, 0, 'o' }, + { "sloppy", 0, 0, 's' }, + { NULL, 0, 0, 0 } + }; + + mount_config_init(progname); + mnt_context_init_helper(cxt, MNT_ACT_MOUNT, 0); + + while ((c = getopt_long(argc, argv, "fhnrVvwo:s", longopts, NULL)) != -1) { + + rc = mnt_context_helper_setopt(cxt, c, optarg); + if (rc == 0) /* valid option */ + continue; + if (rc < 0) /* error (probably ENOMEM) */ + goto err; + /* rc==1 means unknow option */ + switch (c) { + case 'V': + printf("%s: ("PACKAGE_STRING")\n", progname); + return EX_SUCCESS; + case 'h': + default: + mount_usage(); + return EX_USAGE; + } + } + + if (optind < argc) + spec = argv[optind++]; + if (optind < argc) + mount_point = argv[optind++]; + + if (!mount_point) { + nfs_error(_("%s: no mount point provided"), progname); + goto err; + } + if (!spec) { + nfs_error(_("%s: no mount spec provided"), progname); + goto err; + } + + if (geteuid() != 0) { + nfs_error(_("%s: not installed setuid - " + "\"user\" NFS mounts not supported."), progname); + goto err; + } + + verbose = mnt_context_is_verbose(cxt); + sloppy = mnt_context_is_sloppy(cxt); + nomtab = mnt_context_is_nomtab(cxt); + + if (strcmp(progname, "mount.nfs4") == 0) + mnt_context_set_fstype(cxt, "nfs4"); + else + mnt_context_set_fstype(cxt, "nfs"); /* default */ + + rc = mnt_context_set_source(cxt, spec); + if (!rc) + mnt_context_set_target(cxt, mount_point); + if (rc) { + nfs_error(_("%s: failed to set spec or mountpoint: %s"), + progname, strerror(errno)); + goto err; + } + + mount_point = mnt_resolve_path(mount_point, + mnt_context_get_cache(cxt)); + + if (chk_mountpoint(mount_point)) + goto err; + /* + * Concatenate mount options from the configuration file + */ + fs = mnt_context_get_fs(cxt); + if (fs) { + opts = mnt_fs_strdup_options(fs); + + opts = mount_config_opts(spec, mount_point, opts); + mnt_fs_set_options(fs, opts); + } + + rc = mnt_context_prepare_mount(cxt); + if (rc) { + nfs_error(_("%s: failed to prepare mount: %s\n"), + progname, strerror(-rc)); + goto err; + } + + rc = try_mount(cxt, FOREGROUND); + + if (rc == EX_BG) { + printf(_("%s: backgrounding \"%s\"\n"), + progname, mnt_context_get_source(cxt)); + printf(_("%s: mount options: \"%s\"\n"), + progname, opts); + + fflush(stdout); + + if (daemon(0, 0)) { + nfs_error(_("%s: failed to start " + "background process: %s\n"), + progname, strerror(errno)); + exit(EX_FAIL); + } + + rc = try_mount(cxt, BACKGROUND); + + if (verbose && rc) + printf(_("%s: giving up \"%s\"\n"), + progname, mnt_context_get_source(cxt)); + } + + mnt_context_set_syscall_status(cxt, rc == EX_SUCCESS ? 0 : -1); + mnt_context_finalize_mount(cxt); /* mtab update */ + return rc; +err: + return EX_FAIL; +} + +int main(int argc, char *argv[]) +{ + struct libmnt_context *cxt; + int rc; + + mnt_init_debug(0); + cxt = mnt_new_context(); + if (!cxt) { + nfs_error(_("Can't initilize libmount: %s"), + strerror(errno)); + rc = EX_FAIL; + goto done; + } + + progname = basename(argv[0]); + nfs_mount_data_version = discover_nfs_mount_data_version(&string); + + if(strncmp(progname, "umount", 6) == 0) + rc = umount_main(cxt, argc, argv); + else + rc = mount_main(cxt, argc, argv); +done: + mnt_free_context(cxt); + return rc; +} diff --git a/utils/mount/network.c b/utils/mount/network.c index d612427..d1f91dc 100644 --- a/utils/mount/network.c +++ b/utils/mount/network.c @@ -59,6 +59,8 @@ #define CONNECT_TIMEOUT (20) #define MOUNT_TIMEOUT (30) +#define SAFE_SOCKADDR(x) (struct sockaddr *)(char *)(x) + extern int nfs_mount_data_version; extern char *progname; extern int verbose; @@ -208,9 +210,6 @@ int nfs_lookup(const char *hostname, const sa_family_t family, { struct addrinfo *gai_results; struct addrinfo gai_hint = { -#ifdef HAVE_DECL_AI_ADDRCONFIG - .ai_flags = AI_ADDRCONFIG, -#endif /* HAVE_DECL_AI_ADDRCONFIG */ .ai_family = family, }; socklen_t len = *salen; @@ -428,12 +427,12 @@ static int get_socket(struct sockaddr_in *saddr, unsigned int p_prot, if (bindresvport(so, &laddr) < 0) goto err_bindresvport; } else { - cc = bind(so, (struct sockaddr *)&laddr, namelen); + cc = bind(so, SAFE_SOCKADDR(&laddr), namelen); if (cc < 0) goto err_bind; } if (type == SOCK_STREAM || (conn && type == SOCK_DGRAM)) { - cc = connect_to(so, (struct sockaddr *)saddr, namelen, + cc = connect_to(so, SAFE_SOCKADDR(saddr), namelen, timeout); if (cc < 0) goto err_connect; @@ -756,11 +755,12 @@ int nfs_probe_bothports(const struct sockaddr *mnt_saddr, */ int probe_bothports(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server) { - return nfs_probe_bothports((struct sockaddr *)&mnt_server->saddr, - sizeof(mnt_server->saddr), + struct sockaddr *mnt_addr = SAFE_SOCKADDR(&mnt_server->saddr); + struct sockaddr *nfs_addr = SAFE_SOCKADDR(&nfs_server->saddr); + + return nfs_probe_bothports(mnt_addr, sizeof(mnt_server->saddr), &mnt_server->pmap, - (struct sockaddr *)&nfs_server->saddr, - sizeof(nfs_server->saddr), + nfs_addr, sizeof(nfs_server->saddr), &nfs_server->pmap); } @@ -772,7 +772,7 @@ static int nfs_probe_statd(void) }; rpcprog_t program = nfs_getrpcbyname(NSMPROG, nfs_ns_pgmtbl); - return nfs_getport_ping((struct sockaddr *)&addr, sizeof(addr), + return nfs_getport_ping(SAFE_SOCKADDR(&addr), sizeof(addr), program, (rpcvers_t)1, IPPROTO_UDP); } @@ -901,7 +901,7 @@ int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen, */ int nfs_call_umount(clnt_addr_t *mnt_server, dirpath *argp) { - struct sockaddr *sap = (struct sockaddr *)&mnt_server->saddr; + struct sockaddr *sap = SAFE_SOCKADDR(&mnt_server->saddr); socklen_t salen = sizeof(mnt_server->saddr); struct pmap *pmap = &mnt_server->pmap; CLIENT *clnt; @@ -1011,11 +1011,11 @@ int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog, struct sockaddr_in *caddr) { CLIENT *clnt = NULL; - int sock, stat; + int sock, status; static char clnt_res; struct sockaddr dissolve; - rpc_createerr.cf_stat = stat = 0; + rpc_createerr.cf_stat = status = 0; sock = get_socket(saddr, prot, CONNECT_TIMEOUT, FALSE, TRUE); if (sock == RPC_ANYSOCK) { if (rpc_createerr.cf_error.re_errno == ETIMEDOUT) { @@ -1058,18 +1058,18 @@ int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog, return 0; } memset(&clnt_res, 0, sizeof(clnt_res)); - stat = clnt_call(clnt, NULLPROC, + status = clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void, (caddr_t)NULL, (xdrproc_t)xdr_void, (caddr_t)&clnt_res, TIMEOUT); - if (stat) { + if (status) { clnt_geterr(clnt, &rpc_createerr.cf_error); - rpc_createerr.cf_stat = stat; + rpc_createerr.cf_stat = status; } clnt_destroy(clnt); close(sock); - if (stat == RPC_SUCCESS) + if (status == RPC_SUCCESS) return 1; else return 0; @@ -1095,7 +1095,7 @@ static int nfs_ca_sockname(const struct sockaddr *sap, const socklen_t salen, .sin6_family = AF_INET6, .sin6_addr = IN6ADDR_ANY_INIT, }; - int sock; + int sock, result = 0; sock = socket(sap->sa_family, SOCK_DGRAM, IPPROTO_UDP); if (sock < 0) @@ -1103,28 +1103,26 @@ static int nfs_ca_sockname(const struct sockaddr *sap, const socklen_t salen, switch (sap->sa_family) { case AF_INET: - if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) { - close(sock); - return 0; - } + if (bind(sock, SAFE_SOCKADDR(&sin), sizeof(sin)) < 0) + goto out; break; case AF_INET6: - if (bind(sock, (struct sockaddr *)&sin6, sizeof(sin6)) < 0) { - close(sock); - return 0; - } + if (bind(sock, SAFE_SOCKADDR(&sin6), sizeof(sin6)) < 0) + goto out; break; default: errno = EAFNOSUPPORT; - return 0; + goto out; } - if (connect(sock, sap, salen) < 0) { - close(sock); - return 0; - } + if (connect(sock, sap, salen) < 0) + goto out; - return !getsockname(sock, buf, buflen); + result = !getsockname(sock, buf, buflen); + +out: + close(sock); + return result; } /* @@ -1346,7 +1344,7 @@ nfs_nfs_port(struct mount_options *options, unsigned long *port) case PO_NOT_FOUND: break; case PO_FOUND: - if (tmp >= 1 && tmp <= 65535) { + if (tmp >= 0 && tmp <= 65535) { *port = tmp; return 1; } @@ -1518,7 +1516,11 @@ nfs_mount_protocol(struct mount_options *options, unsigned long *protocol) * 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); + if (!nfs_nfs_protocol(options, protocol)) + return 0; + if (*protocol == NFSPROTO_RDMA) + *protocol = IPPROTO_TCP; + return 1; } /* @@ -1534,7 +1536,7 @@ nfs_mount_port(struct mount_options *options, unsigned long *port) case PO_NOT_FOUND: break; case PO_FOUND: - if (tmp >= 1 && tmp <= 65535) { + if (tmp >= 0 && tmp <= 65535) { *port = tmp; return 1; } @@ -1622,3 +1624,71 @@ int nfs_options2pmap(struct mount_options *options, return 1; } + +/* + * Discover mount server's hostname/address by examining mount options + * + * Returns a pointer to a string that the caller must free, on + * success; otherwise NULL is returned. + */ +static char *nfs_umount_hostname(struct mount_options *options, + char *hostname) +{ + char *option; + + option = po_get(options, "mountaddr"); + if (option) + goto out; + option = po_get(options, "mounthost"); + if (option) + goto out; + option = po_get(options, "addr"); + if (option) + goto out; + + return hostname; + +out: + free(hostname); + return strdup(option); +} + + +/* + * Returns EX_SUCCESS if mount options and device name have been + * parsed successfully; otherwise EX_FAIL. + */ +int nfs_umount_do_umnt(struct mount_options *options, + char **hostname, char **dirname) +{ + union nfs_sockaddr address; + struct sockaddr *sap = &address.sa; + socklen_t salen = sizeof(address); + struct pmap nfs_pmap, mnt_pmap; + sa_family_t family; + + if (!nfs_options2pmap(options, &nfs_pmap, &mnt_pmap)) + 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); + return EX_FAIL; + } + + if (!nfs_mount_proto_family(options, &family)) + return 0; + if (!nfs_lookup(*hostname, family, sap, &salen)) + /* nfs_lookup reports any errors */ + return EX_FAIL; + + if (nfs_advise_umount(sap, salen, &mnt_pmap, dirname) == 0) + /* nfs_advise_umount reports any errors */ + return EX_FAIL; + + return EX_SUCCESS; +} diff --git a/utils/mount/network.h b/utils/mount/network.h index 2a3a110..81c6f22 100644 --- a/utils/mount/network.h +++ b/utils/mount/network.h @@ -75,4 +75,7 @@ int nfs_advise_umount(const struct sockaddr *, const socklen_t, CLIENT *mnt_openclnt(clnt_addr_t *, int *); void mnt_closeclnt(CLIENT *, int); +int nfs_umount_do_umnt(struct mount_options *options, + char **hostname, char **dirname); + #endif /* _NFS_UTILS_MOUNT_NETWORK_H */ diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man index 55d4b55..be91a25 100644 --- a/utils/mount/nfs.man +++ b/utils/mount/nfs.man @@ -69,10 +69,9 @@ for details on specifying raw IPv6 addresses. .P The .I fstype -field contains "nfs", for whatever version of the protocol. -The -.B nfs -allow several mount options, which are described below. +field contains "nfs". Use of the "nfs4" fstype in +.I /etc/fstab +is deprecated. .SH "MOUNT OPTIONS" Refer to .BR mount (8) @@ -464,9 +463,9 @@ 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 "Options for versions 2 and 3 only" +.SS "Options for NFS versions 2 and 3 only" Use these options, along with the options in the above subsection, -for NFSv2/v3 only. They will be ignored for newer versions. +for NFS versions 2 and 3 only. .TP 1.5i .BI proto= netid The transport protocol name and protocol family the NFS client uses @@ -619,7 +618,7 @@ in such cases. .BI nfsvers= n The NFS protocol version number used to contact the server's NFS service. If the server does not support the requested version, the mount request fails. -If this option is not specified, the client negociate a suitable version with +If this option is not specified, the client negotiates a suitable version with the server, trying version 4 first, version 3 second, and version 2 last. .TP 1.5i .BI vers= n @@ -717,9 +716,53 @@ 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. -.SS "Options for version 4 only" +.TP 1.5i +.BR local_lock= mechanism +Specifies whether to use local locking for any or both of the flock and the +POSIX locking mechanisms. +.I mechanism +can be one of +.BR all , +.BR flock , +.BR posix , +or +.BR none . +This option is supported in kernels 2.6.37 and later. +.IP +The Linux NFS client provides a way to make locks local. This means, the +applications can lock files, but such locks provide exclusion only against +other applications running on the same client. Remote applications are not +affected by these locks. +.IP +If this option is not specified, or if +.B none +is specified, the client assumes that the locks are not local. +.IP +If +.BR all +is specified, the client assumes that both flock and POSIX locks are local. +.IP +If +.BR flock +is specified, the client assumes that only flock locks are local and uses +NLM sideband protocol to lock files when POSIX locks are used. +.IP +If +.BR posix +is specified, the client assumes that POSIX locks are local and uses NLM +sideband protocol to lock files when flock locks are used. +.IP +To support legacy flock behavior similar to that of NFS clients < 2.6.12, use +'local_lock=flock'. This option is required when exporting NFS mounts via +Samba as Samba maps Windows share mode locks as flock. Since NFS clients > +2.6.12 implement flock by emulating POSIX locks, this will result in +conflicting locks. +.IP +NOTE: When used together, the 'local_lock' mount option will be overridden +by 'nolock'/'lock' mount option. +.SS "Options for NFS version 4 only" Use these options, along with the options in the first subsection above, -for NFSv4 only. They will be ignored with older versions. +for NFS version 4 and newer. .TP 1.5i .BI proto= netid The transport protocol name and protocol family the NFS client uses @@ -1480,32 +1523,54 @@ of Access Control Lists that are semantically richer than POSIX ACLs. 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. -.SH FILES -.TP 1.5i -.I /etc/fstab -file system table -.SH BUGS -The generic -.B remount -option is not fully supported. -Generic options, such as -.BR rw " and " ro -can be modified using the -.B remount -option, -but NFS-specific options are not all supported. +.SH "THE REMOUNT OPTION" +Generic mount options such as +.BR rw " and " sync +can be modified on NFS mount points using the +.BR remount +option. +See +.BR mount (8) +for more information on generic mount options. +.P +With few exceptions, NFS-specific options +are not able to be modified during a remount. The underlying transport or NFS version cannot be changed by a remount, for example. +.P Performing a remount on an NFS file system mounted with the .B noac option may have unintended consequences. The .B noac -option is a mixture of a generic option, +option is a combination of the generic option .BR sync , -and an NFS-specific option +and the NFS-specific option .BR actimeo=0 . +.SS "Unmounting after a remount" +For mount points that use NFS versions 2 or 3, the NFS umount subcommand +depends on knowing the original set of mount options used to perform the +MNT operation. +These options are stored on disk by the NFS mount subcommand, +and can be erased by a remount. .P +To ensure that the saved mount options are not erased during a remount, +specify either the local mount directory, or the server hostname and +export pathname, but not both, during a remount. For example, +.P +.NF +.TA 2.5i + mount -o remount,ro /mnt +.FI +.P +merges the mount option +.B ro +with the mount options already saved on disk for the NFS server mounted at /mnt. +.SH FILES +.TP 1.5i +.I /etc/fstab +file system table +.SH BUGS Before 2.4.7, the Linux NFS client did not support NFS over TCP. .P Before 2.4.20, the Linux NFS client used a heuristic diff --git a/utils/mount/nfsumount.c b/utils/mount/nfsumount.c index 1514340..8cd2852 100644 --- a/utils/mount/nfsumount.c +++ b/utils/mount/nfsumount.c @@ -31,11 +31,16 @@ #include "nls.h" #include "mount_constants.h" +#include "nfs_mount.h" #include "mount.h" #include "error.h" #include "network.h" #include "parse_opt.h" #include "parse_dev.h" +#include "utils.h" + +#define MOUNTSFILE "/proc/mounts" +#define LINELEN (4096) #if !defined(MNT_FORCE) /* dare not try to include -- lots of errors */ @@ -109,7 +114,7 @@ static int del_mtab(const char *spec, const char *node) res = try_remount(spec, node); if (res) goto writemtab; - return 0; + return EX_SUCCESS; } else umnt_err = errno; } @@ -127,7 +132,7 @@ static int del_mtab(const char *spec, const char *node) } if (res >= 0) - return 0; + return EX_SUCCESS; if (umnt_err) umount_error(umnt_err, node); @@ -135,110 +140,88 @@ static int del_mtab(const char *spec, const char *node) } /* - * Discover mount server's hostname/address by examining mount options - * - * Returns a pointer to a string that the caller must free, on - * success; otherwise NULL is returned. - */ -static char *nfs_umount_hostname(struct mount_options *options, - char *hostname) -{ - char *option; - - option = po_get(options, "mountaddr"); - if (option) - goto out; - option = po_get(options, "mounthost"); - if (option) - goto out; - option = po_get(options, "addr"); - if (option) - goto out; - - return hostname; - -out: - free(hostname); - return strdup(option); -} - -/* - * Returns EX_SUCCESS if mount options and device name have been - * parsed successfully; otherwise EX_FAIL. - */ -static int nfs_umount_do_umnt(struct mount_options *options, - char **hostname, char **dirname) -{ - union { - struct sockaddr sa; - struct sockaddr_in s4; - struct sockaddr_in6 s6; - } address; - struct sockaddr *sap = &address.sa; - socklen_t salen = sizeof(address); - struct pmap nfs_pmap, mnt_pmap; - sa_family_t family; - - if (!nfs_options2pmap(options, &nfs_pmap, &mnt_pmap)) - 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); - return EX_FAIL; - } - - if (!nfs_mount_proto_family(options, &family)) - return 0; - if (!nfs_lookup(*hostname, family, sap, &salen)) - /* nfs_lookup reports any errors */ - return EX_FAIL; - - if (nfs_advise_umount(sap, salen, &mnt_pmap, dirname) == 0) - /* nfs_advise_umount reports any errors */ - return EX_FAIL; - - return EX_SUCCESS; -} - -/* - * Pick up certain mount options used during the original mount - * from /etc/mtab. The basics include the server's IP address and - * the server pathname of the share to unregister. + * Detect NFSv4 mounts. * - * These options might also describe the mount port, mount protocol - * version, and transport protocol used to punch through a firewall. - * We will need this information to get through the firewall again - * to do the umount. + * Consult /proc/mounts to determine if the mount point + * is an NFSv4 mount. The kernel is authoritative about + * what type of mount this is. * - * Note that option parsing failures won't necessarily cause the - * umount request to fail. Those values will be left zero in the - * pmap tuple. If the GETPORT call later fails to disambiguate them, - * then we fail. + * Returns 1 if "mc" is an NFSv4 mount, zero if not, and + * -1 if some error occurred. */ -static int nfs_umount23(const char *devname, char *string) +static int nfs_umount_is_vers4(const struct mntentchn *mc) { - char *hostname, *dirname; - struct mount_options *options; - int result = EX_FAIL; + char buffer[LINELEN], *next; + int retval; + FILE *f; + + if ((f = fopen(MOUNTSFILE, "r")) == NULL) { + fprintf(stderr, "%s: %s\n", + MOUNTSFILE, strerror(errno)); + return -1; + } - if (!nfs_parse_devname(devname, &hostname, &dirname)) - return EX_USAGE; + retval = -1; + while (fgets(buffer, sizeof(buffer), f) != NULL) { + char *device, *mntdir, *type, *flags; + struct mount_options *options; + char *line = buffer; + + next = strchr(line, '\n'); + if (next != NULL) + *next = '\0'; + + device = strtok(line, " \t"); + if (device == NULL) + continue; + mntdir = strtok(NULL, " \t"); + if (mntdir == NULL) + continue; + if (strcmp(device, mc->m.mnt_fsname) != 0 && + strcmp(mntdir, mc->m.mnt_dir) != 0) + continue; + + type = strtok(NULL, " \t"); + if (type == NULL) + continue; + if (strcmp(type, "nfs4") == 0) + goto out_nfs4; + + flags = strtok(NULL, " \t"); + if (flags == NULL) + continue; + options = po_split(flags); + if (options != NULL) { + unsigned long version; + int rc; + + rc = nfs_nfs_version(options, &version); + po_destroy(options); + if (rc && version == 4) + goto out_nfs4; + } - options = po_split(string); - if (options) { - result = nfs_umount_do_umnt(options, &hostname, &dirname); - po_destroy(options); - } else - nfs_error(_("%s: option parsing error"), progname); + goto out_nfs; + } + if (retval == -1) + fprintf(stderr, "%s was not found in %s\n", + mc->m.mnt_dir, MOUNTSFILE); - free(hostname); - free(dirname); - return result; +out: + fclose(f); + return retval; + +out_nfs4: + if (verbose) + fprintf(stderr, "NFSv4 mount point detected\n"); + retval = 1; + goto out; + +out_nfs: + if (verbose) + fprintf(stderr, "Legacy NFS mount point detected\n"); + retval = 0; + goto out; } static struct option umount_longopts[] = @@ -251,17 +234,6 @@ static struct option umount_longopts[] = { NULL, 0, 0, 0 } }; -static void umount_usage(void) -{ - printf(_("usage: %s dir [-fvnrlh]\n"), progname); - printf(_("options:\n\t-f\t\tforce unmount\n")); - printf(_("\t-v\tverbose\n")); - printf(_("\t-n\tDo not update /etc/mtab\n")); - printf(_("\t-r\tremount\n")); - printf(_("\t-l\tlazy unmount\n")); - printf(_("\t-h\tprint this help\n\n")); -} - int nfsumount(int argc, char *argv[]) { int c, ret; @@ -362,16 +334,25 @@ int nfsumount(int argc, char *argv[]) } } - ret = 0; + ret = EX_SUCCESS; if (mc) { - if (!lazy && strcmp(mc->m.mnt_type, "nfs4") != 0) - /* We ignore the error from nfs_umount23. - * If the actual umount succeeds (in del_mtab), - * we don't want to signal an error, as that - * could cause /sbin/mount to retry! - */ - nfs_umount23(mc->m.mnt_fsname, mc->m.mnt_opts); - ret = del_mtab(mc->m.mnt_fsname, mc->m.mnt_dir) ?: ret; + if (!lazy) { + switch (nfs_umount_is_vers4(mc)) { + case 0: + /* We ignore the error from nfs_umount23. + * If the actual umount succeeds (in del_mtab), + * we don't want to signal an error, as that + * could cause /sbin/mount to retry! + */ + nfs_umount23(mc->m.mnt_fsname, mc->m.mnt_opts); + break; + case 1: + break; + default: + return EX_FAIL; + } + } + ret = del_mtab(mc->m.mnt_fsname, mc->m.mnt_dir); } else if (*spec != '/') { if (!lazy) ret = nfs_umount23(spec, "tcp,v3"); diff --git a/utils/mount/parse_opt.c b/utils/mount/parse_opt.c index f0918f7..ab869d9 100644 --- a/utils/mount/parse_opt.c +++ b/utils/mount/parse_opt.c @@ -508,7 +508,7 @@ po_found_t po_get_numeric(struct mount_options *options, char *keyword, long *va int po_rightmost(struct mount_options *options, const char *keys[]) { struct mount_option *option; - unsigned int i; + int i; if (options) { for (option = options->tail; option; option = option->prev) { diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c index 50a1a2a..f1aa503 100644 --- a/utils/mount/stropts.c +++ b/utils/mount/stropts.c @@ -49,10 +49,6 @@ #include "parse_dev.h" #include "conffile.h" -#ifndef HAVE_DECL_AI_ADDRCONFIG -#define AI_ADDRCONFIG 0 -#endif - #ifndef NFS_PROGRAM #define NFS_PROGRAM (100003) #endif @@ -114,7 +110,7 @@ static void nfs_default_version(struct nfsmount_info *mi) } } #else -inline void nfs_default_version(struct nfsmount_info *mi) {} +inline void nfs_default_version(__attribute__ ((unused)) struct nfsmount_info *mi) {} #endif /* MOUNT_CONFIG */ /* @@ -123,10 +119,12 @@ inline void nfs_default_version(struct nfsmount_info *mi) {} * Returns a time_t timeout timestamp, in seconds. */ static time_t nfs_parse_retry_option(struct mount_options *options, - unsigned int timeout_minutes) + const time_t default_timeout) { + time_t timeout_minutes; long tmp; + timeout_minutes = default_timeout; switch (po_get_numeric(options, "retry", &tmp)) { case PO_NOT_FOUND: break; @@ -135,6 +133,7 @@ static time_t nfs_parse_retry_option(struct mount_options *options, timeout_minutes = tmp; break; } + /*FALLTHROUGH*/ case PO_BAD_VALUE: if (verbose) nfs_error(_("%s: invalid retry timeout was specified; " @@ -142,7 +141,7 @@ static time_t nfs_parse_retry_option(struct mount_options *options, break; } - return time(NULL) + (time_t)(timeout_minutes * 60); + return time(NULL) + (timeout_minutes * 60); } /* @@ -343,7 +342,6 @@ static int nfs_validate_options(struct nfsmount_info *mi) { struct addrinfo hint = { .ai_protocol = (int)IPPROTO_UDP, - .ai_flags = AI_ADDRCONFIG, }; sa_family_t family; int error; @@ -570,16 +568,18 @@ static int nfs_sys_mount(struct nfsmount_info *mi, struct mount_options *opts) char *options = NULL; int result; + if (mi->fake) + return 1; + if (po_join(opts, &options) == PO_FAILED) { errno = EIO; return 0; } - if (mi->fake) - return 1; - result = mount(mi->spec, mi->node, mi->type, mi->flags & ~(MS_USER|MS_USERS), options); + free(options); + if (verbose && result) { int save = errno; nfs_error(_("%s: mount(2): %s"), progname, strerror(save)); @@ -650,7 +650,7 @@ out_fail: static int nfs_try_mount_v3v2(struct nfsmount_info *mi) { struct addrinfo *ai; - int ret; + int ret = 0; for (ai = mi->address; ai != NULL; ai = ai->ai_next) { ret = nfs_do_mount_v3v2(mi, ai->ai_addr, ai->ai_addrlen); @@ -737,7 +737,7 @@ out_fail: static int nfs_try_mount_v4(struct nfsmount_info *mi) { struct addrinfo *ai; - int ret; + int ret = 0; for (ai = mi->address; ai != NULL; ai = ai->ai_next) { ret = nfs_do_mount_v4(mi, ai->ai_addr, ai->ai_addrlen); diff --git a/utils/mount/utils.c b/utils/mount/utils.c new file mode 100644 index 0000000..298db39 --- /dev/null +++ b/utils/mount/utils.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2010 Karel Zak + * + * 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. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "sockaddr.h" +#include "nfs_mount.h" +#include "nls.h" +#include "xcommon.h" +#include "version.h" +#include "error.h" +#include "utils.h" +#include "mount.h" +#include "network.h" +#include "parse_dev.h" + +extern int verbose; +extern char *progname; + +/* + * Choose the version of the nfs_mount_data structure that is appropriate + * for the kernel that is doing the mount. + * + * NFS_MOUNT_VERSION: maximum version supported by these sources + * nfs_mount_data_version: maximum version supported by the running kernel + */ +int discover_nfs_mount_data_version(int *string_ver) +{ + unsigned int kernel_version = linux_version_code(); + int ver = 0; + + *string_ver = 0; + + if (kernel_version) { + if (kernel_version < MAKE_VERSION(2, 1, 32)) + ver = 1; + else if (kernel_version < MAKE_VERSION(2, 2, 18)) + ver = 3; + else if (kernel_version < MAKE_VERSION(2, 3, 0)) + ver = 4; + else if (kernel_version < MAKE_VERSION(2, 3, 99)) + ver = 3; + else if (kernel_version < MAKE_VERSION(2, 6, 3)) + ver = 4; + else + ver = 6; + } + if (ver > NFS_MOUNT_VERSION) + ver = NFS_MOUNT_VERSION; + else + if (kernel_version > MAKE_VERSION(2, 6, 22)) + (*string_ver)++; + + return ver; +} + +void print_one(char *spec, char *node, char *type, char *opts) +{ + if (!verbose) + return; + + if (opts) + printf(_("%s on %s type %s (%s)\n"), spec, node, type, opts); + else + printf(_("%s on %s type %s\n"), spec, node, type); +} + +void mount_usage(void) +{ + printf(_("usage: %s remotetarget dir [-rvVwfnsih] [-o nfsoptions]\n"), + progname); + printf(_("options:\n")); + printf(_("\t-r\t\tMount file system readonly\n")); + printf(_("\t-v\t\tVerbose\n")); + printf(_("\t-V\t\tPrint version\n")); + printf(_("\t-w\t\tMount file system read-write\n")); + printf(_("\t-f\t\tFake mount, do not actually mount\n")); + printf(_("\t-n\t\tDo not update /etc/mtab\n")); + printf(_("\t-s\t\tTolerate sloppy mount options rather than fail\n")); + printf(_("\t-h\t\tPrint this help\n")); + printf(_("\tnfsoptions\tRefer to mount.nfs(8) or nfs(5)\n\n")); +} + +void umount_usage(void) +{ + printf(_("usage: %s dir [-fvnrlh]\n"), progname); + printf(_("options:\n\t-f\t\tforce unmount\n")); + printf(_("\t-v\tverbose\n")); + printf(_("\t-n\tDo not update /etc/mtab\n")); + printf(_("\t-r\tremount\n")); + printf(_("\t-l\tlazy unmount\n")); + printf(_("\t-h\tprint this help\n\n")); +} + +int chk_mountpoint(const char *mount_point) +{ + struct stat sb; + + if (stat(mount_point, &sb) < 0){ + mount_error(NULL, mount_point, errno); + return 1; + } + if (S_ISDIR(sb.st_mode) == 0){ + mount_error(NULL, mount_point, ENOTDIR); + return 1; + } + if (access(mount_point, X_OK) < 0) { + mount_error(NULL, mount_point, errno); + return 1; + } + + return 0; +} + +/* + * Pick up certain mount options used during the original mount + * from /etc/mtab. The basics include the server's IP address and + * the server pathname of the share to unregister. + * + * These options might also describe the mount port, mount protocol + * version, and transport protocol used to punch through a firewall. + * We will need this information to get through the firewall again + * to do the umount. + * + * Note that option parsing failures won't necessarily cause the + * umount request to fail. Those values will be left zero in the + * pmap tuple. If the GETPORT call later fails to disambiguate them, + * then we fail. + */ +int nfs_umount23(const char *devname, char *string) +{ + char *hostname = NULL, *dirname = NULL; + struct mount_options *options; + int result = EX_FAIL; + + if (!nfs_parse_devname(devname, &hostname, &dirname)) + return EX_USAGE; + + options = po_split(string); + if (options) { + result = nfs_umount_do_umnt(options, &hostname, &dirname); + po_destroy(options); + } else + nfs_error(_("%s: option parsing error"), progname); + + free(hostname); + free(dirname); + return result; +} diff --git a/utils/mount/utils.h b/utils/mount/utils.h new file mode 100644 index 0000000..3fcd504 --- /dev/null +++ b/utils/mount/utils.h @@ -0,0 +1,36 @@ +/* + * utils.h -- misc utils for mount and umount + * + * Copyright (C) 2010 Karel Zak + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + * + */ + +#ifndef _NFS_UTILS_MOUNT_UTILS_H +#define _NFS_UTILS_MOUNT_UTILS_H + +#include "parse_opt.h" + +int discover_nfs_mount_data_version(int *string_ver); +void print_one(char *spec, char *node, char *type, char *opts); +void mount_usage(void); +void umount_usage(void); +int chk_mountpoint(const char *mount_point); + +int nfs_umount23(const char *devname, char *string); + +#endif /* !_NFS_UTILS_MOUNT_UTILS_H */ diff --git a/utils/mount/version.h b/utils/mount/version.h index 46552a1..af61a6f 100644 --- a/utils/mount/version.h +++ b/utils/mount/version.h @@ -42,9 +42,9 @@ static inline unsigned int linux_version_code(void) if (uname(&my_utsname)) return 0; - p = atoi(strtok(my_utsname.release, ".")); - q = atoi(strtok(NULL, ".")); - r = atoi(strtok(NULL, ".")); + p = (unsigned int)atoi(strtok(my_utsname.release, ".")); + q = (unsigned int)atoi(strtok(NULL, ".")); + r = (unsigned int)atoi(strtok(NULL, ".")); return MAKE_VERSION(p, q, r); } diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c index f70f4d6..1d6e953 100644 --- a/utils/mountd/cache.c +++ b/utils/mountd/cache.c @@ -114,7 +114,7 @@ static void auth_unix_ip(FILE *f) qword_print(f, "nfsd"); qword_print(f, ipaddr); - qword_printint(f, time(0)+30*60); + qword_printuint(f, time(0) + DEFAULT_TTL); if (use_ipaddr) qword_print(f, ipaddr); else if (client) @@ -161,7 +161,7 @@ static void auth_unix_gid(FILE *f) } } qword_printuint(f, uid); - qword_printuint(f, time(0)+30*60); + qword_printuint(f, time(0) + DEFAULT_TTL); if (rv >= 0) { qword_printuint(f, ngroups); for (i=0; ie_path) != 0; int flag_mask = different_fs ? ~NFSEXP_FSID : ~0; + qword_printuint(f, time(0) + exp->e_ttl); qword_printint(f, exp->e_flags & flag_mask); qword_printint(f, exp->e_anonuid); qword_printint(f, exp->e_anongid); @@ -667,7 +667,8 @@ static int dump_to_cache(FILE *f, char *domain, char *path, struct exportent *ex qword_print(f, "uuid"); qword_printhex(f, u, 16); } - } + } else + qword_printuint(f, time(0) + DEFAULT_TTL); return qword_eol(f); } @@ -874,8 +875,8 @@ int cache_process_req(fd_set *readfds) /* * Give IP->domain and domain+path->options to kernel - * % echo nfsd $IP $[now+30*60] $domain > /proc/net/rpc/auth.unix.ip/channel - * % echo $domain $path $[now+30*60] $options $anonuid $anongid $fsid > /proc/net/rpc/nfsd.export/channel + * % echo nfsd $IP $[now+DEFAULT_TTL] $domain > /proc/net/rpc/auth.unix.ip/channel + * % echo $domain $path $[now+DEFAULT_TTL] $options $anonuid $anongid $fsid > /proc/net/rpc/nfsd.export/channel */ static int cache_export_ent(char *domain, struct exportent *exp, char *path) @@ -955,7 +956,7 @@ int cache_export(nfs_export *exp, char *path) qword_print(f, "nfsd"); qword_print(f, host_ntop(get_addrlist(exp->m_client, 0), buf, sizeof(buf))); - qword_printint(f, time(0)+30*60); + qword_printuint(f, time(0) + exp->m_export.e_ttl); qword_print(f, exp->m_client->m_hostname); err = qword_eol(f); diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c index d309950..035624c 100644 --- a/utils/mountd/mountd.c +++ b/utils/mountd/mountd.c @@ -99,12 +99,9 @@ static int version_any(void) static void unregister_services (void) { - if (version2()) { - nfs_svc_unregister(MOUNTPROG, MOUNTVERS); - nfs_svc_unregister(MOUNTPROG, MOUNTVERS_POSIX); - } - if (version3()) - nfs_svc_unregister(MOUNTPROG, MOUNTVERS_NFSV3); + nfs_svc_unregister(MOUNTPROG, MOUNTVERS); + nfs_svc_unregister(MOUNTPROG, MOUNTVERS_POSIX); + nfs_svc_unregister(MOUNTPROG, MOUNTVERS_NFSV3); } static void @@ -840,6 +837,7 @@ main(int argc, char **argv) if (new_cache) cache_open(); + unregister_services(); if (version2()) { listeners += nfs_svc_create("mountd", MOUNTPROG, MOUNTVERS, mount_dispatch, port); diff --git a/utils/mountd/mountd.man b/utils/mountd/mountd.man index 4bb96e8..016a357 100644 --- a/utils/mountd/mountd.man +++ b/utils/mountd/mountd.man @@ -106,11 +106,11 @@ This option can be used to request that .B rpc.mountd do not offer certain versions of NFS. The current version of .B rpc.mountd -can support both NFS version 2 and the newer version 3. If the -NFS kernel module was compiled without support for NFSv3, +can support both NFS version 2, 3 and 4. If the +either one of these version should not be offered, .B rpc.mountd must be invoked with the option -.B "\-\-no-nfs-version 3" . +.B "\-\-no-nfs-version " . .TP .B \-n " or " \-\-no-tcp Don't advertise TCP for mount. diff --git a/utils/mountd/rmtab.c b/utils/mountd/rmtab.c index d339296..527377f 100644 --- a/utils/mountd/rmtab.c +++ b/utils/mountd/rmtab.c @@ -205,6 +205,7 @@ mountlist_list(void) } if (stb.st_mtime != last_mtime) { mountlist_freeall(mlist); + mlist = NULL; last_mtime = stb.st_mtime; setrmtabent("r"); diff --git a/utils/mountd/v4root.c b/utils/mountd/v4root.c index 7fd6af3..c33a5a9 100644 --- a/utils/mountd/v4root.c +++ b/utils/mountd/v4root.c @@ -144,8 +144,11 @@ static int v4root_add_parents(nfs_export *exp) char *ptr; path = strdup(exp->m_export.e_path); - if (!path) + if (!path) { + xlog(L_WARNING, "v4root_add_parents: Unable to create " + "pseudo export for '%s'", exp->m_export.e_path); return -ENOMEM; + } for (ptr = path + 1; ptr; ptr = strchr(ptr, '/')) { int ret; char saved; @@ -173,7 +176,7 @@ void v4root_set() { nfs_export *exp; - int i, ret; + int i; if (!v4root_needed) return; @@ -189,7 +192,7 @@ v4root_set() */ continue; - ret = v4root_add_parents(exp); + v4root_add_parents(exp); /* XXX: error handling! */ } } diff --git a/utils/nfsidmap/Makefile.am b/utils/nfsidmap/Makefile.am new file mode 100644 index 0000000..f837b91 --- /dev/null +++ b/utils/nfsidmap/Makefile.am @@ -0,0 +1,9 @@ +## Process this file with automake to produce Makefile.in + +man8_MANS = nfsidmap.man + +sbin_PROGRAMS = nfsidmap +nfsidmap_SOURCES = nfsidmap.c +nfsidmap_LDADD = -lnfsidmap -lkeyutils + +MAINTAINERCLEANFILES = Makefile.in diff --git a/utils/nfsidmap/nfsidmap.c b/utils/nfsidmap/nfsidmap.c new file mode 100644 index 0000000..2d87381 --- /dev/null +++ b/utils/nfsidmap/nfsidmap.c @@ -0,0 +1,118 @@ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +/* gcc nfsidmap.c -o nfsidmap -l nfsidmap -l keyutils */ + +#define MAX_ID_LEN 11 +#define IDMAP_NAMESZ 128 +#define USER 1 +#define GROUP 0 + + +/* + * Find either a user or group id based on the name@domain string + */ +int id_lookup(char *name_at_domain, key_serial_t key, int type) +{ + char id[MAX_ID_LEN]; + uid_t uid = 0; + gid_t gid = 0; + int rc; + + if (type == USER) { + rc = nfs4_owner_to_uid(name_at_domain, &uid); + sprintf(id, "%u", uid); + } else { + rc = nfs4_group_owner_to_gid(name_at_domain, &gid); + sprintf(id, "%u", gid); + } + + if (rc == 0) + rc = keyctl_instantiate(key, id, strlen(id) + 1, 0); + + return rc; +} + +/* + * Find the name@domain string from either a user or group id + */ +int name_lookup(char *id, key_serial_t key, int type) +{ + char name[IDMAP_NAMESZ]; + char domain[NFS4_MAX_DOMAIN_LEN]; + uid_t uid; + gid_t gid; + int rc; + + rc = nfs4_get_default_domain(NULL, domain, NFS4_MAX_DOMAIN_LEN); + if (rc != 0) { + rc = -1; + goto out; + } + + if (type == USER) { + uid = atoi(id); + rc = nfs4_uid_to_name(uid, domain, name, IDMAP_NAMESZ); + } else { + gid = atoi(id); + rc = nfs4_gid_to_name(gid, domain, name, IDMAP_NAMESZ); + } + + if (rc == 0) + rc = keyctl_instantiate(key, &name, strlen(name), 0); + +out: + return rc; +} + +int main(int argc, char **argv) +{ + char *arg; + char *value; + char *type; + int rc = 1; + int timeout = 600; + key_serial_t key; + + if (argc < 3) + return 1; + + arg = malloc(sizeof(char) * strlen(argv[2]) + 1); + strcpy(arg, argv[2]); + type = strtok(arg, ":"); + value = strtok(NULL, ":"); + + if (argc == 4) { + timeout = atoi(argv[3]); + if (timeout < 0) + timeout = 0; + } + + key = strtol(argv[1], NULL, 10); + + if (strcmp(type, "uid") == 0) + rc = id_lookup(value, key, USER); + else if (strcmp(type, "gid") == 0) + rc = id_lookup(value, key, GROUP); + else if (strcmp(type, "user") == 0) + rc = name_lookup(value, key, USER); + else if (strcmp(type, "group") == 0) + rc = name_lookup(value, key, GROUP); + + /* Set timeout to 5 (600 seconds) minutes */ + if (rc == 0) + keyctl_set_timeout(key, timeout); + + free(arg); + return rc; +} diff --git a/utils/nfsidmap/nfsidmap.man b/utils/nfsidmap/nfsidmap.man new file mode 100644 index 0000000..6c1a2d4 --- /dev/null +++ b/utils/nfsidmap/nfsidmap.man @@ -0,0 +1,60 @@ +.\" +.\"@(#)nfsidmap(8) - The NFS idmapper upcall program +.\" +.\" Copyright (C) 2010 Bryan Schumaker +.TH nfsidmap 5 "1 October 2010" +.SH NAME +nfsidmap \- The NFS idmapper upcall program +.SH DESCRIPTION +The file +.I /usr/sbin/nfsidmap +is used by the NFS idmapper to translate user and group ids into names, and to +translate user and group names into ids. Idmapper uses request-key to perform +the upcall and cache the result. +.I /usr/sbin/nfsidmap +should only be called by request-key, and will perform the translation and +initialize a key with the resulting information. +.PP +NFS_USE_NEW_IDMAPPER must be selected when configuring the kernel to use this +feature. +.SH CONFIGURING +The file +.I /etc/request-key.conf +will need to be modified so +.I /sbin/request-key +can properly direct the upcall. The following line should be added before a call +to keyctl negate: +.PP +create nfs_idmap * * /usr/sbin/nfsidmap %k %d 600 +.PP +This will direct all nfs_idmap requests to the program +.I /usr/sbin/nfsidmap +The last parameter, 600, defines how many seconds into the future the key will +expire. This is an optional parameter for +.I /usr/sbin/nfsidmap +and will default to 600 seconds when not specified. +.PP +The idmapper system uses four key descriptions: +.PP + uid: Find the UID for the given user +.br + gid: Find the GID for the given group +.br + user: Find the user name for the given UID +.br + group: Find the group name for the given GID +.PP +You can choose to handle any of these individually, rather than using the +generic upcall program. If you would like to use your own program for a uid +lookup then you would edit your request-key.conf so it looks similar to this: +.PP +create nfs_idmap uid:* * /some/other/program %k %d 600 +.br +create nfs_idmap * * /usr/sbin/nfsidmap %k %d 600 +.PP +Notice that the new line was added above the line for the generic program. +request-key will find the first matching line and run the corresponding program. +In this case, /some/other/program will handle all uid lookups, and +/usr/sbin/nfsidmap will handle gid, user, and group lookups. +.SH AUTHOR +Bryan Schumaker, diff --git a/utils/nfsstat/nfsstat.c b/utils/nfsstat/nfsstat.c index bacef8e..f31bb81 100644 --- a/utils/nfsstat/nfsstat.c +++ b/utils/nfsstat/nfsstat.c @@ -46,7 +46,7 @@ static unsigned int cltproc3info[CLTPROC3_SZ+2], static unsigned int srvproc4info[SRVPROC4_SZ+2], srvproc4info_old[SRVPROC4_SZ+2]; /* NFSv4 call counts ([0] == 2) */ static unsigned int cltproc4info[CLTPROC4_SZ+2], - cltproc4info_old[CLTPROC4_SZ+2]; /* NFSv4 call counts ([0] == 48) */ + cltproc4info_old[CLTPROC4_SZ+2]; /* NFSv4 call counts ([0] == 49) */ static unsigned int srvproc4opsinfo[SRVPROC4OPS_SZ+2], srvproc4opsinfo_old[SRVPROC4OPS_SZ+2]; /* NFSv4 call counts ([0] == 59) */ static unsigned int srvnetinfo[5], srvnetinfo_old[5]; /* 0 # of received packets @@ -221,8 +221,8 @@ DECLARE_CLT(cltinfo); DECLARE_CLT(cltinfo, _old); static void print_all_stats(int, int, int); -static void print_server_stats(int, int); -static void print_client_stats(int, int); +static void print_server_stats(int); +static void print_client_stats(int); static void print_stats_list(int, int, int); static void print_numbers(const char *, unsigned int *, unsigned int); @@ -239,7 +239,7 @@ static int mounts(const char *); static void get_stats(const char *, struct statinfo *, int *, int, int); -static int has_stats(const unsigned int *); +static int has_stats(const unsigned int *, int); static int has_rpcstats(const unsigned int *, int); static void diff_stats(struct statinfo *, struct statinfo *, int); static void unpause(int); @@ -468,7 +468,7 @@ main(int argc, char **argv) pause(); } - if (opt_since || opt_sleep) { + if (opt_since || (opt_sleep && !sleep_time)) { if (opt_srv) { get_stats(NFSSRVSTAT, serverinfo_tmp, &opt_srv, opt_clt, 1); diff_stats(serverinfo_tmp, serverinfo, 1); @@ -516,16 +516,16 @@ main(int argc, char **argv) static void print_all_stats (int opt_srv, int opt_clt, int opt_prt) { - print_server_stats(opt_srv, opt_prt); - print_client_stats(opt_clt, opt_prt); + if (opt_srv) + print_server_stats(opt_prt); + + if (opt_clt) + print_client_stats(opt_prt); } static void -print_server_stats(int opt_srv, int opt_prt) +print_server_stats(int opt_prt) { - if (!opt_srv) - return; - if (opt_prt & PRNT_NET) { if (opt_sleep && !has_rpcstats(srvnetinfo, 4)) { } else { @@ -582,31 +582,29 @@ print_server_stats(int opt_srv, int opt_prt) printf("\n"); } if (opt_prt & PRNT_CALLS) { + int has_v2_stats = has_stats(srvproc2info, SRVPROC2_SZ+2); + int has_v3_stats = has_stats(srvproc3info, SRVPROC3_SZ+2); + int has_v4_stats = has_stats(srvproc4info, SRVPROC4_SZ+2); + if ((opt_prt & PRNT_V2) || - ((opt_prt & PRNT_AUTO) && has_stats(srvproc2info))) { - if (opt_sleep && !has_stats(srvproc2info)) { - ; - } else { + ((opt_prt & PRNT_AUTO) && has_v2_stats)) { + if (!opt_sleep || has_v2_stats) { print_callstats(LABEL_srvproc2, nfsv2name, srvproc2info + 1, sizeof(nfsv2name)/sizeof(char *)); } } if ((opt_prt & PRNT_V3) || - ((opt_prt & PRNT_AUTO) && has_stats(srvproc3info))) { - if (opt_sleep && !has_stats(srvproc3info)) { - ; - } else { + ((opt_prt & PRNT_AUTO) && has_v3_stats)) { + if (!opt_sleep || has_v3_stats) { print_callstats(LABEL_srvproc3, nfsv3name, srvproc3info + 1, sizeof(nfsv3name)/sizeof(char *)); } } if ((opt_prt & PRNT_V4) || - ((opt_prt & PRNT_AUTO) && has_stats(srvproc4info))) { - if (opt_sleep && !has_stats(srvproc4info)) { - ; - } else { + ((opt_prt & PRNT_AUTO) && has_v4_stats)) { + if (!opt_sleep || has_v4_stats) { print_callstats( LABEL_srvproc4, nfssrvproc4name, srvproc4info + 1, sizeof(nfssrvproc4name)/sizeof(char *)); @@ -618,11 +616,8 @@ print_server_stats(int opt_srv, int opt_prt) } } static void -print_client_stats(int opt_clt, int opt_prt) +print_client_stats(int opt_prt) { - if (!opt_clt) - return; - if (opt_prt & PRNT_NET) { if (opt_sleep && !has_rpcstats(cltnetinfo, 4)) { ; @@ -644,31 +639,28 @@ print_client_stats(int opt_clt, int opt_prt) } } if (opt_prt & PRNT_CALLS) { + int has_v2_stats = has_stats(cltproc2info, CLTPROC2_SZ+2); + int has_v3_stats = has_stats(cltproc3info, CLTPROC3_SZ+2); + int has_v4_stats = has_stats(cltproc4info, CLTPROC4_SZ+2); if ((opt_prt & PRNT_V2) || - ((opt_prt & PRNT_AUTO) && has_stats(cltproc2info))) { - if (opt_sleep && !has_stats(cltproc2info)) { - ; - } else { + ((opt_prt & PRNT_AUTO) && has_v2_stats)) { + if (!opt_sleep || has_v2_stats) { print_callstats(LABEL_cltproc2, nfsv2name, cltproc2info + 1, sizeof(nfsv2name)/sizeof(char *)); } } if ((opt_prt & PRNT_V3) || - ((opt_prt & PRNT_AUTO) && has_stats(cltproc3info))) { - if (opt_sleep && !has_stats(cltproc3info)) { - ; - } else { + ((opt_prt & PRNT_AUTO) && has_v3_stats)) { + if (!opt_sleep || has_v3_stats) { print_callstats(LABEL_cltproc3, nfsv3name, cltproc3info + 1, sizeof(nfsv3name)/sizeof(char *)); } } if ((opt_prt & PRNT_V4) || - ((opt_prt & PRNT_AUTO) && has_stats(cltproc4info))) { - if (opt_sleep && !has_stats(cltproc4info)) { - ; - } else { + ((opt_prt & PRNT_AUTO) && has_v4_stats)) { + if (!opt_sleep || has_v4_stats) { print_callstats(LABEL_cltproc4, nfscltproc4name, cltproc4info + 1, sizeof(nfscltproc4name)/sizeof(char *)); @@ -681,34 +673,28 @@ static void print_clnt_list(int opt_prt) { if (opt_prt & PRNT_CALLS) { + int has_v2_stats = has_stats(cltproc2info, CLTPROC2_SZ+2); + int has_v3_stats = has_stats(cltproc3info, CLTPROC3_SZ+2); + int has_v4_stats = has_stats(cltproc4info, CLTPROC4_SZ+2); if ((opt_prt & PRNT_V2) || - ((opt_prt & PRNT_AUTO) && has_stats(cltproc2info))) { - if (opt_sleep && !has_stats(cltproc2info)) { - ; - } else { + ((opt_prt & PRNT_AUTO) && has_v2_stats)) { + if (!opt_sleep || has_v2_stats) { print_callstats_list("nfs v2 client", nfsv2name, cltproc2info + 1, sizeof(nfsv2name)/sizeof(char *)); } } if ((opt_prt & PRNT_V3) || - ((opt_prt & PRNT_AUTO) && has_stats(cltproc3info))) { - if (opt_sleep && !has_stats(cltproc3info)) { - ; - } else { + ((opt_prt & PRNT_AUTO) && has_v3_stats)) { + if (!opt_sleep || has_v3_stats) { print_callstats_list("nfs v3 client", nfsv3name, cltproc3info + 1, sizeof(nfsv3name)/sizeof(char *)); } } if ((opt_prt & PRNT_V4) || - ((opt_prt & PRNT_AUTO) && has_stats(cltproc4info))) { - if (opt_sleep && !has_stats(cltproc4info)) { - ; - } else { - print_callstats_list("nfs v4 ops", - nfssrvproc4opname, srvproc4opsinfo + 1, - sizeof(nfssrvproc4opname)/sizeof(char *)); + ((opt_prt & PRNT_AUTO) && has_v4_stats)) { + if (!opt_sleep || has_v4_stats) { print_callstats_list("nfs v4 client", nfscltproc4name, cltproc4info + 1, sizeof(nfscltproc4name)/sizeof(char *)); @@ -720,32 +706,32 @@ static void print_serv_list(int opt_prt) { if (opt_prt & PRNT_CALLS) { + int has_v2_stats = has_stats(srvproc2info, SRVPROC2_SZ+2); + int has_v3_stats = has_stats(srvproc3info, SRVPROC3_SZ+2); + int has_v4_stats = has_stats(srvproc4info, SRVPROC4_SZ+2); if ((opt_prt & PRNT_V2) || - ((opt_prt & PRNT_AUTO) && has_stats(srvproc2info))) { - if (opt_sleep && !has_stats(srvproc2info)) { - ; - } else { + ((opt_prt & PRNT_AUTO) && has_v2_stats)) { + if (!opt_sleep || has_v2_stats) { print_callstats_list("nfs v2 server", nfsv2name, srvproc2info + 1, sizeof(nfsv2name)/sizeof(char *)); } } if ((opt_prt & PRNT_V3) || - ((opt_prt & PRNT_AUTO) && has_stats(srvproc3info))) { - if (opt_sleep && !has_stats(srvproc3info)) { - ; - } else { + ((opt_prt & PRNT_AUTO) && has_v3_stats)) { + if (!opt_sleep || has_v3_stats) { print_callstats_list("nfs v3 server", nfsv3name, srvproc3info + 1, sizeof(nfsv3name)/sizeof(char *)); } } if ((opt_prt & PRNT_V4) || - ((opt_prt & PRNT_AUTO) && has_stats(srvproc4opsinfo))) { - if (opt_sleep && !has_stats(srvproc4info)) { - ; - } else { - print_callstats_list("nfs v4 ops", + ((opt_prt & PRNT_AUTO) && has_v4_stats)) { + if (!opt_sleep || has_v4_stats) { + print_callstats_list("nfs v4 server", + nfssrvproc4name, srvproc4info + 1, + sizeof(nfssrvproc4name)/sizeof(char *)); + print_callstats_list("nfs v4 servop", nfssrvproc4opname, srvproc4opsinfo + 1, sizeof(nfssrvproc4opname)/sizeof(char *)); } @@ -1054,9 +1040,9 @@ out: * there are stats if the sum's greater than the entry-count. */ static int -has_stats(const unsigned int *info) +has_stats(const unsigned int *info, int nr) { - return (info[0] && info[info[0] + 1] > info[0]); + return (info[0] && info[nr-1] > info[0]); } static int has_rpcstats(const unsigned int *info, int size) diff --git a/utils/nfsstat/nfsstat.man b/utils/nfsstat/nfsstat.man index 52215a9..cd573b0 100644 --- a/utils/nfsstat/nfsstat.man +++ b/utils/nfsstat/nfsstat.man @@ -30,10 +30,12 @@ Print only NFS v2 statistics. The default is to only print information about the versions of \fBNFS\fR that have non-zero counts. .TP .B \-3 -Print only NFS v3 statistics. +Print only NFS v3 statistics. The default is to only print information +about the versions of \fBNFS\fR that have non-zero counts. .TP .B \-4 -Print only NFS v4 statistics. +Print only NFS v4 statistics. The default is to only print information +about the versions of \fBNFS\fR that have non-zero counts. .TP .B \-m, \-\-mounts Print information about each of the mounted \fBNFS\fR file systems. diff --git a/utils/statd/hostname.c b/utils/statd/hostname.c index 38f2265..616a3cb 100644 --- a/utils/statd/hostname.c +++ b/utils/statd/hostname.c @@ -39,10 +39,6 @@ #include "statd.h" #include "xlog.h" -#ifndef HAVE_DECL_AI_ADDRCONFIG -#define AI_ADDRCONFIG 0 -#endif - /** * statd_present_address - convert sockaddr to presentation address * @sap: pointer to socket address to convert diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c index 437e37a..1f490b0 100644 --- a/utils/statd/sm-notify.c +++ b/utils/statd/sm-notify.c @@ -34,8 +34,9 @@ #include "nsm.h" #include "nfsrpc.h" -#ifndef HAVE_DECL_AI_ADDRCONFIG -#define AI_ADDRCONFIG 0 +/* glibc before 2.3.4 */ +#ifndef AI_NUMERICSERV +#define AI_NUMERICSERV 0 #endif #define NSM_TIMEOUT 2 @@ -78,7 +79,6 @@ smn_lookup(const char *name) { struct addrinfo *ai = NULL; struct addrinfo hint = { - .ai_flags = AI_ADDRCONFIG, .ai_family = (nsm_family == AF_INET ? AF_INET: AF_UNSPEC), .ai_protocol = (int)IPPROTO_UDP, }; @@ -253,6 +253,7 @@ smn_bind_address(const char *srcaddr, const char *srcport) if (srcaddr == NULL) hint.ai_flags |= AI_PASSIVE; + /* Do not allow "node" and "service" parameters both to be NULL */ if (srcport == NULL) error = getaddrinfo(srcaddr, "", &hint, &ai); else diff --git a/utils/statd/statd.man b/utils/statd/statd.man index ca00e24..b72236c 100644 --- a/utils/statd/statd.man +++ b/utils/statd/statd.man @@ -12,7 +12,7 @@ .SH NAME rpc.statd \- NSM service daemon .SH SYNOPSIS -.BI "rpc.statd [-dh?FLNvVw] [-H " prog "] [-n " my-name "] [-o " outgoing-port "] [-p " listener-port "] [-P " path " ] +.BI "rpc.statd [-dh?FLNvV] [-H " prog "] [-n " my-name "] [-o " outgoing-port "] [-p " listener-port "] [-P " path " ] .SH DESCRIPTION File locks are not part of persistent file system state. Lock state is thus lost when a host reboots.