From: Luk Claes Date: Sun, 2 Oct 2011 14:47:26 +0000 (+0200) Subject: Merge branch 'upstream' X-Git-Tag: debian/1%1.2.5-1~11 X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=commitdiff_plain;h=3ad7dea527170ae12c9cdc2e428d0676ac261144;hp=e9ce0a846a8b751c974535b662651c5ff4ae8abf Merge branch 'upstream' --- diff --git a/.gitignore b/.gitignore index aa579b8..ea08c2e 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ support/include/stamp-h1 lib*.a tools/rpcgen/rpcgen tools/rpcdebug/rpcdebug +utils/blkmapd/blkmapd utils/exportfs/exportfs utils/idmapd/idmapd utils/lockd/lockd diff --git a/COPYING b/COPYING index 60549be..941c87d 100644 --- a/COPYING +++ b/COPYING @@ -1,12 +1,12 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. - Preamble + Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public @@ -15,7 +15,7 @@ software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to +the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not @@ -55,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. - - GNU GENERAL PUBLIC LICENSE + + GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains @@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions: License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) - + These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in @@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. - + 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is @@ -225,7 +225,7 @@ impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. - + 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License @@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. - NO WARRANTY + NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN @@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it @@ -291,7 +291,7 @@ convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - Copyright (C) 19yy + Copyright (C) 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 @@ -303,17 +303,16 @@ the "copyright" line and a pointer to where the full notice is found. 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 02111-1307 USA - + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: - Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. @@ -336,5 +335,5 @@ necessary. Here is a sample; alter the names: This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General +library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. diff --git a/aclocal/keyutils.m4 b/aclocal/keyutils.m4 index 84bc112..a392c0e 100644 --- a/aclocal/keyutils.m4 +++ b/aclocal/keyutils.m4 @@ -6,6 +6,6 @@ AC_DEFUN([AC_KEYUTILS], [ AC_CHECK_LIB([keyutils], [keyctl_instantiate], [LIBKEYUTILS=-lkeyutils], ,) AC_SUBST(LIBKEYUTILS) - AC_CHECK_HEADERS([keyutils.h], , - [AC_MSG_ERROR([keyutils.h header not found.])]) + AC_CHECK_HEADERS([keyutils.h]) + ])dnl diff --git a/aclocal/libnfsidmap.m4 b/aclocal/libnfsidmap.m4 index 4faa923..484b1ec 100644 --- a/aclocal/libnfsidmap.m4 +++ b/aclocal/libnfsidmap.m4 @@ -15,7 +15,6 @@ AC_DEFUN([AC_LIBNFSIDMAP], [ [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]) + AC_CHECK_LIB([nfsidmap], [nfs4_owner_to_uid]) ])dnl diff --git a/aclocal/rpcsec_vers.m4 b/aclocal/rpcsec_vers.m4 index 25902ca..8218372 100644 --- a/aclocal/rpcsec_vers.m4 +++ b/aclocal/rpcsec_vers.m4 @@ -1,7 +1,7 @@ dnl Checks librpcsec version AC_DEFUN([AC_RPCSEC_VERSION], [ - PKG_CHECK_MODULES([GSSGLUE], [libgssglue >= 0.1]) + PKG_CHECK_MODULES([GSSGLUE], [libgssglue >= 0.3]) dnl TI-RPC replaces librpcsecgss if test "$enable_tirpc" = no; then diff --git a/configure.ac b/configure.ac index ca12f9e..80fb39d 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ dnl Process this file with autoconf to produce a configure script. dnl -AC_INIT([linux nfs-utils],[1.2.4],[linux-nfs@vger.kernel.org],[nfs-utils]) +AC_INIT([linux nfs-utils],[1.2.5],[linux-nfs@vger.kernel.org],[nfs-utils]) AC_CANONICAL_BUILD([]) AC_CANONICAL_HOST([]) AC_CONFIG_MACRO_DIR(aclocal) @@ -18,10 +18,19 @@ AC_ARG_WITH(release, RELEASE=1) AC_SUBST(RELEASE) AC_ARG_WITH(statedir, - [ --with-statedir=/foo use state dir /foo [/var/lib/nfs]], + [AC_HELP_STRING([--with-statedir=/foo], + [use state dir /foo @<:@default=/var/lib/nfs@:>@])], statedir=$withval, statedir=/var/lib/nfs) AC_SUBST(statedir) +AC_ARG_WITH(statdpath, + [AC_HELP_STRING([--with-statdpath=/foo @<:@default=/var/lib/nfs@:>@], + [define statd's state dir as /foo instead of the NFS statedir] + )], + statdpath=$withval, + statdpath=$statedir + ) + AC_SUBST(statdpath) AC_ARG_WITH(statduser, [AC_HELP_STRING([--with-statduser=rpcuser], [statd to run under @<:@rpcuser or nobody@:>@] @@ -46,24 +55,12 @@ AC_ARG_WITH(start-statd, ) AC_SUBST(startstatd) AC_DEFINE_UNQUOTED(START_STATD, "$startstatd", [Define this to a script which can start statd on mount]) -AC_ARG_ENABLE(nfsv3, - [AC_HELP_STRING([--enable-nfsv3], - [enable support for NFSv3 @<:@default=yes@:>@])], - enable_nfsv3=$enableval, - enable_nfsv3=yes) - if test "$enable_nfsv3" = yes; then - AC_DEFINE(NFS3_SUPPORTED, 1, [Define this if you want NFSv3 support compiled in]) - else - enable_nfsv3= - fi - AC_SUBST(enable_nfsv3) AC_ARG_ENABLE(nfsv4, [AC_HELP_STRING([--enable-nfsv4], [enable support for NFSv4 @<:@default=yes@:>@])], enable_nfsv4=$enableval, enable_nfsv4=yes) if test "$enable_nfsv4" = yes; then - AC_DEFINE(NFS4_SUPPORTED, 1, [Define this if you want NFSv4 support compiled in]) IDMAPD=idmapd else enable_nfsv4= @@ -75,13 +72,17 @@ AC_ARG_ENABLE(nfsv4, AC_ARG_ENABLE(nfsv41, [AC_HELP_STRING([--enable-nfsv41], - [enable support for NFSv41 @<:@default=no@:>@])], + [enable support for NFSv41 @<:@default=yes@:>@])], enable_nfsv41=$enableval, - enable_nfsv41=no) + enable_nfsv41=yes) if test "$enable_nfsv41" = yes; then - AC_DEFINE(NFS41_SUPPORTED, 1, [Define this if you want NFSv41 support compiled in]) + if test "$enable_nfsv4" != yes; then + AC_MSG_ERROR([NFS v4.1 is enabled but NFS v4 is not. Use --disable-nfsv41]) + fi + BLKMAPD=blkmapd else enable_nfsv41= + BLKMAPD= fi AC_SUBST(enable_nfsv41) AM_CONDITIONAL(CONFIG_NFSV41, [test "$enable_nfsv41" = "yes"]) @@ -92,7 +93,6 @@ AC_ARG_ENABLE(gss, enable_gss=$enableval, enable_gss=yes) if test "$enable_gss" = yes; then - AC_DEFINE(GSS_SUPPORTED, 1, [Define this if you want rpcsec_gss support compiled in]) GSSD=gssd SVCGSSD=svcgssd else @@ -114,21 +114,21 @@ AC_ARG_WITH(rpcgen, rpcgen_path=$withval, rpcgen_path=yes ) RPCGEN_PATH= - if test "$rpcgen_path" == "yes"; then + if test "$rpcgen_path" = "yes"; then for p in /usr/local/bin/rpcgen /usr/bin/rpcgen /bin/rpcgen do if test -f $p ; then RPCGEN_PATH=$p ; break; fi ; done elif test "$rpcgen_path" != "internal"; then RPCGEN_PATH=$rpcgen_path fi AC_SUBST(RPCGEN_PATH) - AM_CONDITIONAL(CONFIG_RPCGEN, [test "$RPCGEN_PATH" == ""]) + AM_CONDITIONAL(CONFIG_RPCGEN, [test "$RPCGEN_PATH" = ""]) AC_ARG_ENABLE(uuid, [AC_HELP_STRING([--disable-uuid], [Exclude uuid support to avoid buggy libblkid])], if test "$enableval" = "yes" ; then choose_blkid=yes; else choose_blkid=no; fi, choose_blkid=default) AC_ARG_ENABLE(mount, [AC_HELP_STRING([--enable-mount], - [Create mount.nfs and don't use the util-linux mount(8) functionality. @<:@default=yes@:>@])], + [Create mount.nfs and do not use the util-linux mount(8) functionality. @<:@default=yes@:>@])], enable_mount=$enableval, enable_mount=yes) AM_CONDITIONAL(CONFIG_MOUNT, [test "$enable_mount" = "yes"]) @@ -137,7 +137,7 @@ 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=$enableval, enable_libmount=no) fi @@ -256,9 +256,6 @@ 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 @@ -268,6 +265,9 @@ if test "$enable_nfsv4" = yes; then AC_RPCSEC_VERSION fi fi +dnl enable nfsidmap when its support by libnfsidmap +AM_CONDITIONAL(CONFIG_NFSIDMAP, [test "$ac_cv_header_keyutils_h$ac_cv_lib_nfsidmap_nfs4_owner_to_uid" = "yesyes"]) + if test "$knfsd_cv_glibc2" = no; then AC_CHECK_LIB(bsd, daemon, [LIBBSD="-lbsd"]) @@ -386,6 +386,7 @@ dnl ************************************************************* dnl Export some path names to config.h dnl ************************************************************* AC_DEFINE_UNQUOTED(NFS_STATEDIR, "$statedir", [This defines the location of the NFS state files. Warning: this must match definitions in config.mk!]) +AC_DEFINE_UNQUOTED(NSM_DEFAULT_STATEDIR, "$statdpath", [Define this to the pathname where statd keeps its state file]) if test "x$cross_compiling" = "xno"; then CFLAGS_FOR_BUILD=${CFLAGS_FOR_BUILD-"$CFLAGS"} @@ -450,6 +451,7 @@ AC_CONFIG_FILES([ tools/mountstats/Makefile tools/nfs-iostat/Makefile utils/Makefile + utils/blkmapd/Makefile utils/exportfs/Makefile utils/gssd/Makefile utils/idmapd/Makefile diff --git a/support/include/nfs/nfs.h b/support/include/nfs/nfs.h index c939d78..320880e 100644 --- a/support/include/nfs/nfs.h +++ b/support/include/nfs/nfs.h @@ -15,13 +15,6 @@ #define NFSD_MINVERS 2 #define NFSD_MAXVERS 4 -#define NFSD_MINMINORVERS4 1 -#ifdef NFS41_SUPPORTED -#define NFSD_MAXMINORVERS4 1 -#else -#define NFSD_MAXMINORVERS4 0 -#endif - struct nfs_fh_len { int fh_size; u_int8_t fh_handle[NFS3_FHSIZE]; diff --git a/support/include/nfsrpc.h b/support/include/nfsrpc.h index d50fe94..a0b80e1 100644 --- a/support/include/nfsrpc.h +++ b/support/include/nfsrpc.h @@ -15,8 +15,8 @@ * * 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. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA * */ diff --git a/support/include/rpcmisc.h b/support/include/rpcmisc.h index 0b06457..b806227 100644 --- a/support/include/rpcmisc.h +++ b/support/include/rpcmisc.h @@ -53,6 +53,7 @@ void rpc_init(char *name, int prog, int vers, void rpc_dispatch(struct svc_req *rq, SVCXPRT *xprt, struct rpc_dtable *dtable, int nvers, void *argp, void *resp); +int getservport(u_long number, const char *proto); extern int _rpcpmstart; extern int _rpcfdtype; diff --git a/support/nfs/exports.c b/support/nfs/exports.c index c250383..c96500f 100644 --- a/support/nfs/exports.c +++ b/support/nfs/exports.c @@ -784,8 +784,9 @@ struct export_features *get_export_features(void) fd = open(path, O_RDONLY); if (fd == -1) goto good; - fd = read(fd, buf, 50); - if (fd == -1) + c = read(fd, buf, 50); + close(fd); + if (c == -1) goto err; c = sscanf(buf, "%x %x", &ef.flags, &ef.secinfo_flags); if (c != 2) diff --git a/support/nfs/getport.c b/support/nfs/getport.c index d74400b..3331ad4 100644 --- a/support/nfs/getport.c +++ b/support/nfs/getport.c @@ -17,8 +17,8 @@ * * 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. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA * */ diff --git a/support/nfs/rpc_socket.c b/support/nfs/rpc_socket.c index c14efe8..7896cd2 100644 --- a/support/nfs/rpc_socket.c +++ b/support/nfs/rpc_socket.c @@ -15,8 +15,8 @@ * * 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. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA * */ diff --git a/support/nfs/svc_create.c b/support/nfs/svc_create.c index b3f75ed..c159fc8 100644 --- a/support/nfs/svc_create.c +++ b/support/nfs/svc_create.c @@ -393,7 +393,7 @@ nfs_svc_create(char *name, const rpcprog_t program, const rpcvers_t version, const struct sigaction create_sigaction = { .sa_handler = SIG_IGN, }; - unsigned int visible, up; + unsigned int visible, up, servport; struct netconfig *nconf; void *handlep; @@ -417,8 +417,13 @@ nfs_svc_create(char *name, const rpcprog_t program, const rpcvers_t version, if (!(nconf->nc_flag & NC_VISIBLE)) continue; visible++; + if (port == 0) + servport = getservport(program, nconf->nc_proto); + else + servport = port; + up += svc_create_nconf(name, program, version, dispatch, - port, nconf); + servport, nconf); } if (visible == 0) diff --git a/support/nfs/svc_socket.c b/support/nfs/svc_socket.c index 03a5325..f56f310 100644 --- a/support/nfs/svc_socket.c +++ b/support/nfs/svc_socket.c @@ -13,8 +13,8 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 0211-1301 USA */ #include #include @@ -35,14 +35,43 @@ # define __close(f) close ((f)) #endif +int getservport(u_long number, const char *proto) +{ + char rpcdata[1024], servdata[1024]; + struct rpcent rpcbuf, *rpcp; + struct servent servbuf, *servp = NULL; + int ret; + + ret = getrpcbynumber_r(number, &rpcbuf, rpcdata, sizeof rpcdata, + &rpcp); + if (ret == 0 && rpcp != NULL) { + /* First try name. */ + ret = getservbyname_r(rpcp->r_name, proto, &servbuf, servdata, + sizeof servdata, &servp); + if ((ret != 0 || servp == NULL) && rpcp->r_aliases) { + const char **a; + + /* Then we try aliases. */ + for (a = (const char **) rpcp->r_aliases; *a != NULL; a++) { + ret = getservbyname_r(*a, proto, &servbuf, servdata, + sizeof servdata, &servp); + if (ret == 0 && servp != NULL) + break; + } + } + } + + if (ret == 0 && servp != NULL) + return ntohs(servp->s_port); + + return 0; +} + static int svc_socket (u_long number, int type, int protocol, int reuse) { struct sockaddr_in addr; socklen_t len = sizeof (struct sockaddr_in); - char rpcdata [1024], servdata [1024]; - struct rpcent rpcbuf, *rpcp; - struct servent servbuf, *servp = NULL; int sock, ret; const char *proto = protocol == IPPROTO_TCP ? "tcp" : "udp"; @@ -66,48 +95,13 @@ svc_socket (u_long number, int type, int protocol, int reuse) memset (&addr, 0, sizeof (addr)); addr.sin_family = AF_INET; + addr.sin_port = htons(getservport(number, proto)); - ret = getrpcbynumber_r (number, &rpcbuf, rpcdata, sizeof rpcdata, - &rpcp); - if (ret == 0 && rpcp != NULL) - { - /* First try name. */ - ret = getservbyname_r (rpcp->r_name, proto, &servbuf, servdata, - sizeof servdata, &servp); - if ((ret != 0 || servp == NULL) && rpcp->r_aliases) - { - const char **a; - - /* Then we try aliases. */ - for (a = (const char **) rpcp->r_aliases; *a != NULL; a++) - { - ret = getservbyname_r (*a, proto, &servbuf, servdata, - sizeof servdata, &servp); - if (ret == 0 && servp != NULL) - break; - } - } - } - - if (ret == 0 && servp != NULL) + if (bind(sock, (struct sockaddr *) &addr, len) < 0) { - addr.sin_port = servp->s_port; - if (bind (sock, (struct sockaddr *) &addr, len) < 0) - { - perror (_("svc_socket: bind problem")); - (void) __close (sock); - sock = -1; - } - } - else - { - addr.sin_port = 0; - if (bind (sock, (struct sockaddr *) &addr, len) < 0) - { - perror (_("svc_socket: bind problem")); - (void) __close (sock); - sock = -1; - } + perror (_("svc_socket: bind problem")); + (void) __close(sock); + sock = -1; } if (sock >= 0) diff --git a/support/nsm/file.c b/support/nsm/file.c index 98b47bf..5dd52c1 100644 --- a/support/nsm/file.c +++ b/support/nsm/file.c @@ -94,14 +94,6 @@ #define NSM_KERNEL_STATE_FILE "/proc/sys/fs/nfs/nsm_local_state" -/* - * Some distributions place statd's files in a subdirectory - */ -#define NSM_PATH_EXTENSION -/* #define NSM_PATH_EXTENSION "/statd" */ - -#define NSM_DEFAULT_STATEDIR NFS_STATEDIR NSM_PATH_EXTENSION - static char nsm_base_dirname[PATH_MAX] = NSM_DEFAULT_STATEDIR; #define NSM_MONITOR_DIR "sm" @@ -395,18 +387,18 @@ nsm_drop_privileges(const int pidfd) return false; } - if (st.st_uid == 0) { - xlog_warn("Running as root. " - "chown %s to choose different user", nsm_base_dirname); - return true; - } - if (chdir(nsm_base_dirname) == -1) { xlog(L_ERROR, "Failed to change working directory to %s: %m", nsm_base_dirname); return false; } + if (st.st_uid == 0) { + xlog_warn("Running as root. " + "chown %s to choose different user", nsm_base_dirname); + return true; + } + /* * If the pidfile happens to reside on NFS, dropping privileges * will probably cause us to lose access, even though we are diff --git a/tests/t0001-statd-basic-mon-unmon.sh b/tests/t0001-statd-basic-mon-unmon.sh index 00127fb..51f0d22 100755 --- a/tests/t0001-statd-basic-mon-unmon.sh +++ b/tests/t0001-statd-basic-mon-unmon.sh @@ -16,7 +16,7 @@ # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA # . ./test-lib.sh diff --git a/tests/test-lib.sh b/tests/test-lib.sh index 3d47264..ce4ecff 100644 --- a/tests/test-lib.sh +++ b/tests/test-lib.sh @@ -16,7 +16,7 @@ # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA # # make sure $srcdir is set and sanity check it diff --git a/tools/mountstats/mountstats.py b/tools/mountstats/mountstats.py index c475c9e..b95b71d 100644 --- a/tools/mountstats/mountstats.py +++ b/tools/mountstats/mountstats.py @@ -17,7 +17,8 @@ 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 02111-1307 USA +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +MA 02110-1301 USA """ import sys, os, time @@ -45,6 +46,12 @@ class DeviceData: self.__nfs_data['fstype'] = words[7] if words[7].find('nfs') != -1: self.__nfs_data['statvers'] = words[8] + elif 'nfs' in words or 'nfs4' in words: + self.__nfs_data['export'] = words[0] + self.__nfs_data['mountpoint'] = words[3] + self.__nfs_data['fstype'] = words[6] + if words[6].find('nfs') != -1: + self.__nfs_data['statvers'] = words[7] elif words[0] == 'age:': self.__nfs_data['age'] = long(words[1]) elif words[0] == 'opts:': @@ -370,6 +377,9 @@ def parse_stats_file(filename): if words[0] == 'device': key = words[4] new = [ line.strip() ] + elif 'nfs' in words or 'nfs4' in words: + key = words[3] + new = [ line.strip() ] else: new += [ line.strip() ] ms_dict[key] = new diff --git a/tools/nfs-iostat/nfs-iostat.py b/tools/nfs-iostat/nfs-iostat.py index 1207674..d909632 100644 --- a/tools/nfs-iostat/nfs-iostat.py +++ b/tools/nfs-iostat/nfs-iostat.py @@ -17,7 +17,8 @@ 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 02111-1307 USA +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +MA 02110-1301 USA """ import sys, os, time @@ -85,6 +86,12 @@ class DeviceData: self.__nfs_data['fstype'] = words[7] if words[7] == 'nfs': self.__nfs_data['statvers'] = words[8] + elif 'nfs' in words or 'nfs4' in words: + self.__nfs_data['export'] = words[0] + self.__nfs_data['mountpoint'] = words[3] + self.__nfs_data['fstype'] = words[6] + if words[6] == 'nfs': + self.__nfs_data['statvers'] = words[7] elif words[0] == 'age:': self.__nfs_data['age'] = long(words[1]) elif words[0] == 'opts:': @@ -425,6 +432,9 @@ def parse_stats_file(filename): if words[0] == 'device': key = words[4] new = [ line.strip() ] + elif 'nfs' in words or 'nfs4' in words: + key = words[3] + new = [ line.strip() ] else: new += [ line.strip() ] ms_dict[key] = new @@ -435,7 +445,6 @@ def parse_stats_file(filename): def print_iostat_summary(old, new, devices, time, options): stats = {} diff_stats = {} - if old: # Trim device list to only include intersection of old and new data, # this addresses umounts due to autofs mountpoints @@ -552,7 +561,6 @@ client are listed. parser.add_option_group(displaygroup) (options, args) = parser.parse_args(sys.argv) - for arg in args: if arg == sys.argv[0]: diff --git a/tools/nfs-iostat/nfsiostat.man b/tools/nfs-iostat/nfsiostat.man index 99e04fb..3ec245d 100644 --- a/tools/nfs-iostat/nfsiostat.man +++ b/tools/nfs-iostat/nfsiostat.man @@ -24,7 +24,7 @@ parameter is specified, the value of .I determines the number of reports generated at -. +.I seconds apart. if the interval parameter is specified without the .I diff --git a/utils/Makefile.am b/utils/Makefile.am index a0ea116..d074b85 100644 --- a/utils/Makefile.am +++ b/utils/Makefile.am @@ -9,6 +9,10 @@ OPTDIRS += nfsidmap endif endif +if CONFIG_NFSV41 +OPTDIRS += blkmapd +endif + if CONFIG_GSS OPTDIRS += gssd endif diff --git a/utils/blkmapd/Makefile.am b/utils/blkmapd/Makefile.am new file mode 100644 index 0000000..203f9f2 --- /dev/null +++ b/utils/blkmapd/Makefile.am @@ -0,0 +1,19 @@ +## Process this file with automake to produce Makefile.in + +man8_MANS = blkmapd.man +EXTRA_DIST = $(man8_MANS) + +AM_CFLAGS += -D_LARGEFILE64_SOURCE +sbin_PROGRAMS = blkmapd + +blkmapd_SOURCES = \ + device-discovery.c \ + device-inq.c \ + device-process.c \ + dm-device.c \ + device-discovery.h + +blkmapd_LDADD = -ldevmapper ../../support/nfs/libnfs.a + +MAINTAINERCLEANFILES = Makefile.in + diff --git a/utils/blkmapd/blkmapd.man b/utils/blkmapd/blkmapd.man new file mode 100644 index 0000000..fd38122 --- /dev/null +++ b/utils/blkmapd/blkmapd.man @@ -0,0 +1,54 @@ +.\" +.\" Copyright 2011, Jim Rees. +.\" +.\" You may distribute under the terms of the GNU General Public +.\" License as specified in the file COPYING that comes with the +.\" nfs-utils distribution. +.\" +.TH blkmapd 8 "11 August 2011" +.SH NAME +blkmapd \- pNFS block layout mapping daemon +.SH SYNOPSIS +.B "blkmapd [-d] [-f]" +.SH DESCRIPTION +The +.B blkmapd +daemon performs device discovery and mapping for the parallel NFS (pNFS) block layout +client [RFC5663]. +.PP +The pNFS block layout protocol builds a complex storage hierarchy from a set +of +.I simple volumes. +These simple volumes are addressed by content, using a signature on the +volume to uniquely name each one. +The daemon locates a volume by examining each block device in the system for +the given signature. +.PP +The topology typically consists of a hierarchy of volumes built by striping, +slicing, and concatenating the simple volumes. +The +.B blkmapd +daemon uses the device-mapper driver to construct logical devices that +reflect the server topology, and passes these devices to the kernel for use +by the pNFS block layout client. +.SH OPTIONS +.TP +.B -d +Performs device discovery only then exits. +.TP +.B -f +Runs +.B blkmapd +in the foreground and sends output to stderr (as opposed to syslogd) +.SH SEE ALSO +.BR nfs (5), +.BR dmsetup (8) +.sp +RFC 5661 for the NFS version 4.1 specification. +.br +RFC 5663 for the pNFS block layout specification. +.SH AUTHORS +.br +Haiying Tang +.br +Jim Rees diff --git a/utils/blkmapd/device-discovery.c b/utils/blkmapd/device-discovery.c new file mode 100644 index 0000000..c21de3e --- /dev/null +++ b/utils/blkmapd/device-discovery.c @@ -0,0 +1,453 @@ +/* + * device-discovery.c: main function, discovering device and processing + * pipe request from kernel. + * + * Copyright (c) 2010 EMC Corporation, Haiying Tang + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "device-discovery.h" + +#define BL_PIPE_FILE "/var/lib/nfs/rpc_pipefs/nfs/blocklayout" +#define PID_FILE "/var/run/blkmapd.pid" + +struct bl_disk *visible_disk_list; + +struct bl_disk_path *bl_get_path(const char *filepath, + struct bl_disk_path *paths) +{ + struct bl_disk_path *tmp = paths; + + while (tmp) { + if (!strcmp(tmp->full_path, filepath)) + break; + tmp = tmp->next; + } + return tmp; +} + +/* Check whether valid_path is a substring(partition) of path */ +int bl_is_partition(struct bl_disk_path *valid_path, struct bl_disk_path *path) +{ + if (!strncmp(valid_path->full_path, path->full_path, + strlen(valid_path->full_path))) + return 1; + + return 0; +} + +/* + * For multipath devices, devices state could be PASSIVE/ACTIVE/PSEUDO, + * where PSEUDO > ACTIVE > PASSIVE. Device with highest state is used to + * create pseudo device. So if state is higher, the device path needs to + * be updated. + * If device-mapper multipath support is a must, pseudo devices should + * exist for each multipath device. If not, active device path will be + * chosen for device creation. + * Treat partition as invalid path. + */ +int bl_update_path(struct bl_disk_path *path, enum bl_path_state_e state, + struct bl_disk *disk) +{ + struct bl_disk_path *valid_path = disk->valid_path; + + if (valid_path) { + if (valid_path->state >= state) { + if (bl_is_partition(valid_path, path)) + return 0; + } + } + return 1; +} + +void bl_release_disk(void) +{ + struct bl_disk *disk; + struct bl_disk_path *path = NULL; + + while (visible_disk_list) { + disk = visible_disk_list; + path = disk->paths; + while (path) { + disk->paths = path->next; + free(path->full_path); + free(path); + path = disk->paths; + } + if (disk->serial) + free(disk->serial); + visible_disk_list = disk->next; + free(disk); + } +} + +void bl_add_disk(char *filepath) +{ + struct bl_disk *disk = NULL; + int fd = 0; + struct stat sb; + off_t size = 0; + struct bl_serial *serial = NULL; + enum bl_path_state_e ap_state; + struct bl_disk_path *diskpath = NULL, *path = NULL; + dev_t dev; + + fd = open(filepath, O_RDONLY | O_LARGEFILE); + if (fd < 0) + return; + + if (fstat(fd, &sb)) { + close(fd); + return; + } + + if (!sb.st_size) + ioctl(fd, BLKGETSIZE, &size); + else + size = sb.st_size; + + if (!size) { + close(fd); + return; + } + + dev = sb.st_rdev; + serial = bldev_read_serial(fd, filepath); + if (dm_is_dm_major(major(dev))) + ap_state = BL_PATH_STATE_PSEUDO; + else + ap_state = bldev_read_ap_state(fd); + close(fd); + + if (ap_state != BL_PATH_STATE_ACTIVE) + return; + + for (disk = visible_disk_list; disk != NULL; disk = disk->next) { + /* Already scanned or a partition? + * XXX: if released each time, maybe not need to compare + */ + if ((serial->len == disk->serial->len) && + !memcmp(serial->data, disk->serial->data, serial->len)) { + diskpath = bl_get_path(filepath, disk->paths); + break; + } + } + + if (disk && diskpath) + return; + + /* add path */ + path = malloc(sizeof(struct bl_disk_path)); + if (!path) { + BL_LOG_ERR("%s: Out of memory!\n", __func__); + goto out_err; + } + path->next = NULL; + path->state = ap_state; + path->full_path = strdup(filepath); + if (!path->full_path) + goto out_err; + + if (!disk) { /* add disk */ + disk = malloc(sizeof(struct bl_disk)); + if (!disk) { + BL_LOG_ERR("%s: Out of memory!\n", __func__); + goto out_err; + } + disk->next = visible_disk_list; + disk->dev = dev; + disk->size = size; + disk->serial = serial; + disk->valid_path = path; + disk->paths = path; + visible_disk_list = disk; + } else { + path->next = disk->paths; + disk->paths = path; + /* check whether we need to update disk info */ + if (bl_update_path(path, path->state, disk)) { + disk->dev = dev; + disk->size = size; + disk->valid_path = path; + } + } + return; + + out_err: + if (path) { + if (path->full_path) + free(path->full_path); + free(path); + } + return; +} + +int bl_discover_devices(void) +{ + FILE *f; + int n; + char buf[PATH_MAX], devname[PATH_MAX], fulldevname[PATH_MAX]; + + /* release previous list */ + bl_release_disk(); + + /* scan all block devices */ + f = fopen("/proc/partitions", "r"); + if (f == NULL) + return 0; + + while (1) { + if (fgets(buf, sizeof buf, f) == NULL) + break; + n = sscanf(buf, "%*d %*d %*d %31s", devname); + if (n != 1) + continue; + snprintf(fulldevname, sizeof fulldevname, "/sys/block/%s", + devname); + if (access(fulldevname, F_OK) < 0) + continue; + snprintf(fulldevname, sizeof fulldevname, "/dev/%s", devname); + bl_add_disk(fulldevname); + } + + fclose(f); + + return 0; +} + +/* process kernel request + * return 0: request processed, and no more request waiting; + * return 1: request processed, and more requests waiting; + * return < 0: error + */ +int bl_disk_inquiry_process(int fd) +{ + int ret = 0; + struct bl_pipemsg_hdr head; + char *buf = NULL; + uint32_t major, minor; + uint16_t buflen; + struct bl_dev_msg reply; + + /* read request */ + if (atomicio(read, fd, &head, sizeof(head)) != sizeof(head)) { + /* Note that an error in this or the next read is pretty + * catastrophic, as there is no good way to resync into + * the pipe's stream. + */ + BL_LOG_ERR("Read pipefs head error!\n"); + ret = -EIO; + goto out; + } + + buflen = head.totallen; + buf = malloc(buflen); + if (!buf) { + BL_LOG_ERR("%s: Out of memory!\n", __func__); + ret = -ENOMEM; + goto out; + } + + if (atomicio(read, fd, buf, buflen) != buflen) { + BL_LOG_ERR("Read pipefs content error!\n"); + ret = -EIO; + goto out; + } + + reply.status = BL_DEVICE_REQUEST_PROC; + + switch (head.type) { + case BL_DEVICE_MOUNT: + /* + * It shouldn't be necessary to discover devices here, since + * process_deviceinfo() will re-discover if it can't find + * the devices it needs. But in the case of multipath + * devices (ones that appear more than once, for example an + * active and a standby LUN), this will re-order them in the + * correct priority. + */ + bl_discover_devices(); + if (!process_deviceinfo(buf, buflen, &major, &minor)) { + reply.status = BL_DEVICE_REQUEST_ERR; + break; + } + reply.major = major; + reply.minor = minor; + break; + case BL_DEVICE_UMOUNT: + if (!dm_device_remove_all((uint64_t *) buf)) + reply.status = BL_DEVICE_REQUEST_ERR; + break; + default: + reply.status = BL_DEVICE_REQUEST_ERR; + break; + } + + /* write to pipefs */ + if (atomicio((void *)write, fd, &reply, sizeof(reply)) + != sizeof(reply)) { + BL_LOG_ERR("Write pipefs error!\n"); + ret = -EIO; + } + + out: + if (buf) + free(buf); + return ret; +} + +/* TODO: set bl_process_stop to 1 in command */ +unsigned int bl_process_stop; + +int bl_run_disk_inquiry_process(int fd) +{ + fd_set rset; + int ret; + + bl_process_stop = 0; + + for (;;) { + if (bl_process_stop) + return 1; + FD_ZERO(&rset); + FD_SET(fd, &rset); + ret = 0; + switch (select(fd + 1, &rset, NULL, NULL, NULL)) { + case -1: + if (errno == EINTR) + continue; + else { + ret = -errno; + goto out; + } + case 0: + goto out; + default: + if (FD_ISSET(fd, &rset)) + ret = bl_disk_inquiry_process(fd); + } + } + out: + return ret; +} + +/* Daemon */ +int main(int argc, char **argv) +{ + int fd, pidfd = -1, opt, dflag = 0, fg = 0, ret = 1; + struct stat statbuf; + char pidbuf[64]; + + while ((opt = getopt(argc, argv, "df")) != -1) { + switch (opt) { + case 'd': + dflag = 1; + break; + case 'f': + fg = 1; + break; + } + } + + if (fg) { + openlog("blkmapd", LOG_PERROR, 0); + } else { + if (!stat(PID_FILE, &statbuf)) { + fprintf(stderr, "Pid file %s already existed\n", PID_FILE); + exit(1); + } + + if (daemon(0, 0) != 0) { + fprintf(stderr, "Daemonize failed\n"); + exit(1); + } + + openlog("blkmapd", LOG_PID, 0); + pidfd = open(PID_FILE, O_WRONLY | O_CREAT, 0644); + if (pidfd < 0) { + BL_LOG_ERR("Create pid file %s failed\n", PID_FILE); + exit(1); + } + + if (lockf(pidfd, F_TLOCK, 0) < 0) { + BL_LOG_ERR("Lock pid file %s failed\n", PID_FILE); + close(pidfd); + exit(1); + } + ftruncate(pidfd, 0); + sprintf(pidbuf, "%d\n", getpid()); + write(pidfd, pidbuf, strlen(pidbuf)); + } + + if (dflag) { + bl_discover_devices(); + exit(0); + } + + /* open pipe file */ + fd = open(BL_PIPE_FILE, O_RDWR); + if (fd < 0) { + BL_LOG_ERR("open pipe file %s error\n", BL_PIPE_FILE); + exit(1); + } + + while (1) { + /* discover device when needed */ + bl_discover_devices(); + + ret = bl_run_disk_inquiry_process(fd); + if (ret < 0) { + /* what should we do with process error? */ + BL_LOG_ERR("inquiry process return %d\n", ret); + } + } + + if (pidfd >= 0) { + close(pidfd); + unlink(PID_FILE); + } + + exit(ret); +} diff --git a/utils/blkmapd/device-discovery.h b/utils/blkmapd/device-discovery.h new file mode 100644 index 0000000..a86eed9 --- /dev/null +++ b/utils/blkmapd/device-discovery.h @@ -0,0 +1,162 @@ +/* + * bl-device-discovery.h + * + * Copyright (c) 2010 EMC Corporation, Haiying Tang + * 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. + */ +#ifndef BL_DEVICE_DISCOVERY_H +#define BL_DEVICE_DISCOVERY_H + +#include + +enum blk_vol_type { + BLOCK_VOLUME_SIMPLE = 0, /* maps to a single LU */ + BLOCK_VOLUME_SLICE = 1, /* slice of another volume */ + BLOCK_VOLUME_CONCAT = 2, /* concatenation of multiple volumes */ + BLOCK_VOLUME_STRIPE = 3, /* striped across multiple volumes */ + BLOCK_VOLUME_PSEUDO = 4, +}; + +/* All disk offset/lengths are stored in 512-byte sectors */ +struct bl_volume { + uint32_t bv_type; + off_t bv_size; + struct bl_volume **bv_vols; + int bv_vol_n; + union { + dev_t bv_dev; /* for BLOCK_VOLUME_SIMPLE(PSEUDO) */ + off_t bv_stripe_unit; /* for BLOCK_VOLUME_STRIPE(CONCAT) */ + off_t bv_offset; /* for BLOCK_VOLUME_SLICE */ + } param; +}; + +struct bl_sig_comp { + int64_t bs_offset; /* In bytes */ + uint32_t bs_length; /* In bytes */ + char *bs_string; +}; + +/* Maximum number of signatures components in a simple volume */ +# define BLOCK_MAX_SIG_COMP 16 + +struct bl_sig { + int si_num_comps; + struct bl_sig_comp si_comps[BLOCK_MAX_SIG_COMP]; +}; + +/* + * Multipath support: ACTIVE or PSEUDO device is valid, + * PASSIVE is a standby for ACTIVE. + */ +enum bl_path_state_e { + BL_PATH_STATE_PASSIVE = 1, + BL_PATH_STATE_ACTIVE = 2, + BL_PATH_STATE_PSEUDO = 3, +}; + +struct bl_serial { + int len; + char *data; +}; + +struct bl_disk_path { + struct bl_disk_path *next; + char *full_path; + enum bl_path_state_e state; +}; + +struct bl_disk { + struct bl_disk *next; + struct bl_serial *serial; + dev_t dev; + off_t size; /* in 512-byte sectors */ + struct bl_disk_path *valid_path; + struct bl_disk_path *paths; +}; + +struct bl_dev_id { + unsigned char type; + unsigned char ids; + unsigned char reserve; + unsigned char len; + char data[0]; +}; + +struct bl_dev_msg { + int status; + uint32_t major, minor; +}; + +struct bl_pipemsg_hdr { + uint8_t type; + uint16_t totallen; /* length of message excluding hdr */ +}; + +#define BL_DEVICE_UMOUNT 0x0 /* Umount--delete devices */ +#define BL_DEVICE_MOUNT 0x1 /* Mount--create devices */ +#define BL_DEVICE_REQUEST_INIT 0x0 /* Start request */ +#define BL_DEVICE_REQUEST_PROC 0x1 /* User process succeeds */ +#define BL_DEVICE_REQUEST_ERR 0x2 /* User process fails */ + +uint32_t *blk_overflow(uint32_t * p, uint32_t * end, size_t nbytes); + +#define BLK_READBUF(p, e, nbytes) do { \ + p = blk_overflow(p, e, nbytes); \ + if (!p) {\ + goto out_err;\ + } \ +} while (0) + +#define READ32(x) (x) = ntohl(*p++) + +#define READ64(x) do { \ + (x) = (uint64_t)ntohl(*p++) << 32; \ + (x) |= ntohl(*p++); \ +} while (0) + +#define READ_SECTOR(x) do { \ + READ64(tmp); \ + if (tmp & 0x1ff) { \ + goto out_err; \ + } \ + (x) = tmp >> 9; \ +} while (0) + +extern struct bl_disk *visible_disk_list; +uint64_t dm_device_create(struct bl_volume *vols, int num_vols); +int dm_device_remove_all(uint64_t *dev); +uint64_t process_deviceinfo(const char *dev_addr_buf, + unsigned int dev_addr_len, + uint32_t *major, uint32_t *minor); + +extern ssize_t atomicio(ssize_t(*f) (int, void *, size_t), + int fd, void *_s, size_t n); +extern struct bl_serial *bldev_read_serial(int fd, const char *filename); +extern enum bl_path_state_e bldev_read_ap_state(int fd); +extern int bl_discover_devices(void); + +#define BL_LOG_INFO(fmt...) syslog(LOG_INFO, fmt) +#define BL_LOG_WARNING(fmt...) syslog(LOG_WARNING, fmt) +#define BL_LOG_ERR(fmt...) syslog(LOG_ERR, fmt) +#define BL_LOG_DEBUG(fmt...) syslog(LOG_DEBUG, fmt) +#endif diff --git a/utils/blkmapd/device-inq.c b/utils/blkmapd/device-inq.c new file mode 100644 index 0000000..eabc70c --- /dev/null +++ b/utils/blkmapd/device-inq.c @@ -0,0 +1,233 @@ +/* + * device-inq.c: inquire SCSI device information. + * + * Copyright (c) 2010 EMC Corporation, Haiying Tang + * All rights reserved. + * + * This program refers to "SCSI Primary Commands - 3 (SPC-3) + * at http://www.t10.org and sg_inq.c in sg3_utils-1.26 for + * Linux OS SCSI subsystem, by D. Gilbert. + * + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "device-discovery.h" + +#define DEF_ALLOC_LEN 255 +#define MX_ALLOC_LEN (0xc000 + 0x80) + +static struct bl_serial *bl_create_scsi_string(int len, const char *bytes) +{ + struct bl_serial *s; + + s = malloc(sizeof(*s) + len); + if (s) { + s->data = (char *)&s[1]; + s->len = len; + memcpy(s->data, bytes, len); + } + return s; +} + +static void bl_free_scsi_string(struct bl_serial *str) +{ + if (str) + free(str); +} + +#define sg_io_ok(io_hdr) \ + ((((io_hdr).status & 0x7e) == 0) && \ + ((io_hdr).host_status == 0) && \ + (((io_hdr).driver_status & 0x0f) == 0)) + +static int sg_timeout = 1 * 1000; + +static int bldev_inquire_page(int fd, int page, char *buffer, int len) +{ + unsigned char cmd[] = { INQUIRY, 0, 0, 0, 0, 0 }; + unsigned char sense_b[28]; + struct sg_io_hdr io_hdr; + if (page >= 0) { + cmd[1] = 1; + cmd[2] = page; + } + cmd[3] = (unsigned char)((len >> 8) & 0xff); + cmd[4] = (unsigned char)(len & 0xff); + + memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = sizeof(cmd); + io_hdr.mx_sb_len = sizeof(sense_b); + io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + io_hdr.dxfer_len = len; + io_hdr.dxferp = buffer; + io_hdr.cmdp = cmd; + io_hdr.sbp = sense_b; + io_hdr.timeout = sg_timeout; + if (ioctl(fd, SG_IO, &io_hdr) < 0) + return -1; + + if (sg_io_ok(io_hdr)) + return 0; + return -1; +} + +static int bldev_inquire_pages(int fd, int page, char **buffer) +{ + int status = 0; + char *tmp; + int len; + + *buffer = calloc(DEF_ALLOC_LEN, sizeof(char)); + if (!*buffer) { + BL_LOG_ERR("%s: Out of memory!\n", __func__); + return -ENOMEM; + } + + status = bldev_inquire_page(fd, page, *buffer, DEF_ALLOC_LEN); + if (status) + goto out; + + status = -1; + if ((*(*buffer + 1) & 0xff) != page) + goto out; + + len = (*(*buffer + 2) << 8) + *(*buffer + 3) + 4; + if (len > MX_ALLOC_LEN) { + BL_LOG_ERR("SCSI response length too long: %d\n", len); + goto out; + } + if (len > DEF_ALLOC_LEN) { + tmp = realloc(*buffer, len); + if (!tmp) { + BL_LOG_ERR("%s: Out of memory!\n", __func__); + status = -ENOMEM; + goto out; + } + *buffer = tmp; + status = bldev_inquire_page(fd, page, *buffer, len); + if (status) + goto out; + } + status = 0; + out: + return status; +} + +/* For EMC multipath devices, use VPD page (0xc0) to get status. + * For other devices, return ACTIVE for now + */ +extern enum bl_path_state_e bldev_read_ap_state(int fd) +{ + int status = 0; + char *buffer = NULL; + enum bl_path_state_e ap_state = BL_PATH_STATE_ACTIVE; + + status = bldev_inquire_pages(fd, 0xc0, &buffer); + if (status) + goto out; + + if (buffer[4] < 0x02) + ap_state = BL_PATH_STATE_PASSIVE; + out: + if (buffer) + free(buffer); + return ap_state; +} + +struct bl_serial *bldev_read_serial(int fd, const char *filename) +{ + struct bl_serial *serial_out = NULL; + int status = 0; + char *buffer; + struct bl_dev_id *dev_root, *dev_id; + unsigned int pos, len, current_id = 0; + + status = bldev_inquire_pages(fd, 0x83, &buffer); + if (status) + goto out; + + dev_root = (struct bl_dev_id *)buffer; + + pos = 0; + current_id = 0; + len = dev_root->len; + while (pos < (len - sizeof(struct bl_dev_id) + sizeof(unsigned char))) { + dev_id = (struct bl_dev_id *)&(dev_root->data[pos]); + if ((dev_id->ids & 0xf) < current_id) + continue; + switch (dev_id->ids & 0xf) { + /* We process SCSI ID with four ID cases: 0, 1, 2 and 3. + * When more than one ID is available, priority is + * 3>2>1>0. + */ + case 2: /* EUI-64 based */ + if ((dev_id->len != 8) && (dev_id->len != 12) && + (dev_id->len != 16)) + break; + case 3: /* NAA */ + /* TODO: NAA validity judgement too complicated, + * so just ingore it here. + */ + if ((dev_id->type & 0xf) != 1) { + BL_LOG_ERR("Binary code_set expected\n"); + break; + } + case 0: /* vendor specific */ + case 1: /* T10 vendor identification */ + current_id = dev_id->ids & 0xf; + if (serial_out) + bl_free_scsi_string(serial_out); + serial_out = bl_create_scsi_string(dev_id->len, + dev_id->data); + break; + } + if (current_id == 3) + break; + pos += (dev_id->len + sizeof(struct bl_dev_id) - + sizeof(unsigned char)); + } + out: + if (!serial_out) + serial_out = bl_create_scsi_string(strlen(filename), filename); + if (buffer) + free(buffer); + return serial_out; +} diff --git a/utils/blkmapd/device-process.c b/utils/blkmapd/device-process.c new file mode 100644 index 0000000..27ff374 --- /dev/null +++ b/utils/blkmapd/device-process.c @@ -0,0 +1,407 @@ +/* + * device-process.c: detailed processing of device information sent + * from kernel. + * + * Copyright (c) 2006 The Regents of the University of Michigan. + * All rights reserved. + * + * Andy Adamson + * Fred Isaman + * + * Copyright (c) 2010 EMC Corporation, Haiying Tang + * + * Used codes in linux/fs/nfs/blocklayout/blocklayoutdev.c. + * + * 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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "device-discovery.h" + +static char *pretty_sig(char *sig, uint32_t siglen) +{ + static char rs[100]; + uint64_t sigval; + unsigned int i; + + if (siglen <= sizeof(sigval)) { + sigval = 0; + for (i = 0; i < siglen; i++) + sigval |= ((unsigned char *)sig)[i] << (i * 8); + sprintf(rs, "0x%0llx", (unsigned long long) sigval); + } else { + if (siglen > sizeof rs - 4) { + siglen = sizeof rs - 4; + sprintf(&rs[siglen], "..."); + } else + rs[siglen] = '\0'; + memcpy(rs, sig, siglen); + } + return rs; +} + +uint32_t *blk_overflow(uint32_t * p, uint32_t * end, size_t nbytes) +{ + uint32_t *q = p + ((nbytes + 3) >> 2); + + if (q > end || q < p) + return NULL; + return p; +} + +static int decode_blk_signature(uint32_t **pp, uint32_t * end, + struct bl_sig *sig) +{ + int i; + uint32_t siglen, *p = *pp; + + BLK_READBUF(p, end, 4); + READ32(sig->si_num_comps); + if (sig->si_num_comps == 0) { + BL_LOG_ERR("0 components in sig\n"); + goto out_err; + } + if (sig->si_num_comps >= BLOCK_MAX_SIG_COMP) { + BL_LOG_ERR("number of sig comps %i >= BLOCK_MAX_SIG_COMP\n", + sig->si_num_comps); + goto out_err; + } + for (i = 0; i < sig->si_num_comps; i++) { + struct bl_sig_comp *comp = &sig->si_comps[i]; + + BLK_READBUF(p, end, 12); + READ64(comp->bs_offset); + READ32(siglen); + comp->bs_length = siglen; + BLK_READBUF(p, end, siglen); + /* Note we rely here on fact that sig is used immediately + * for mapping, then thrown away. + */ + comp->bs_string = (char *)p; + BL_LOG_INFO("%s: si_comps[%d]: bs_length %d, bs_string %s\n", + __func__, i, siglen, + pretty_sig(comp->bs_string, siglen)); + p += ((siglen + 3) >> 2); + } + *pp = p; + return 0; + out_err: + return -EIO; +} + +/* + * Read signature from device and compare to sig_comp + * return: 0=match, 1=no match, -1=error + */ +static int +read_cmp_blk_sig(struct bl_disk *disk, int fd, struct bl_sig_comp *comp) +{ + const char *dev_name = disk->valid_path->full_path; + int ret = -1; + ssize_t siglen = comp->bs_length; + int64_t bs_offset = comp->bs_offset; + char *sig = NULL; + + sig = (char *)malloc(siglen); + if (!sig) { + BL_LOG_ERR("%s: Out of memory\n", __func__); + goto out; + } + + if (bs_offset < 0) + bs_offset += (((int64_t) disk->size) << 9); + if (lseek64(fd, bs_offset, SEEK_SET) == -1) { + BL_LOG_ERR("File %s lseek error\n", dev_name); + goto out; + } + + if (read(fd, sig, siglen) != siglen) { + BL_LOG_ERR("File %s read error\n", dev_name); + goto out; + } + + ret = memcmp(sig, comp->bs_string, siglen); + if (!ret) + BL_LOG_INFO("%s: %s sig %s at %lld\n", __func__, dev_name, + pretty_sig(sig, siglen), + (long long)comp->bs_offset); + + out: + if (sig) + free(sig); + return ret; +} + +/* + * All signatures in sig must be found on disk for verification. + * Returns True if sig matches, False otherwise. + */ +static int verify_sig(struct bl_disk *disk, struct bl_sig *sig) +{ + const char *dev_name = disk->valid_path->full_path; + int fd, i, rv; + + fd = open(dev_name, O_RDONLY | O_LARGEFILE); + if (fd < 0) { + BL_LOG_ERR("%s: %s could not be opened for read\n", __func__, + dev_name); + return 0; + } + + rv = 1; + + for (i = 0; i < sig->si_num_comps; i++) { + if (read_cmp_blk_sig(disk, fd, &sig->si_comps[i])) { + rv = 0; + break; + } + } + + if (fd >= 0) + close(fd); + return rv; +} + +/* + * map_sig_to_device() + * Given a signature, walk the list of visible disks searching for + * a match. Returns True if mapping was done, False otherwise. + * + * While we're at it, fill in the vol->bv_size. + */ +static int map_sig_to_device(struct bl_sig *sig, struct bl_volume *vol) +{ + int mapped = 0; + struct bl_disk *disk; + + /* scan disk list to find out match device */ + for (disk = visible_disk_list; disk; disk = disk->next) { + /* FIXME: should we use better algorithm for disk scan? */ + mapped = verify_sig(disk, sig); + if (mapped) { + vol->param.bv_dev = disk->dev; + vol->bv_size = disk->size; + break; + } + } + return mapped; +} + +/* We are given an array of XDR encoded array indices, each of which should + * refer to a previously decoded device. Translate into a list of pointers + * to the appropriate pnfs_blk_volume's. + */ +static int set_vol_array(uint32_t **pp, uint32_t *end, + struct bl_volume *vols, int working) +{ + int i, index; + uint32_t *p = *pp; + struct bl_volume **array = vols[working].bv_vols; + + for (i = 0; i < vols[working].bv_vol_n; i++) { + BLK_READBUF(p, end, 4); + READ32(index); + if ((index < 0) || (index >= working)) { + BL_LOG_ERR("set_vol_array: Id %i out of range\n", + index); + goto out_err; + } + array[i] = &vols[index]; + } + *pp = p; + return 0; + out_err: + return -EIO; +} + +static uint64_t sum_subvolume_sizes(struct bl_volume *vol) +{ + int i; + uint64_t sum = 0; + + for (i = 0; i < vol->bv_vol_n; i++) + sum += vol->bv_vols[i]->bv_size; + return sum; +} + +static int +decode_blk_volume(uint32_t **pp, uint32_t *end, struct bl_volume *vols, int voln, + int *array_cnt) +{ + int status = 0, j; + struct bl_sig sig; + uint32_t *p = *pp; + struct bl_volume *vol = &vols[voln]; + uint64_t tmp; + + BLK_READBUF(p, end, 4); + READ32(vol->bv_type); + + switch (vol->bv_type) { + case BLOCK_VOLUME_SIMPLE: + *array_cnt = 0; + status = decode_blk_signature(&p, end, &sig); + if (status) + return status; + status = map_sig_to_device(&sig, vol); + if (!status) { + BL_LOG_ERR("Could not find disk for device\n"); + return -ENXIO; + } + BL_LOG_INFO("%s: simple %d\n", __func__, voln); + status = 0; + break; + case BLOCK_VOLUME_SLICE: + BLK_READBUF(p, end, 16); + READ_SECTOR(vol->param.bv_offset); + READ_SECTOR(vol->bv_size); + *array_cnt = vol->bv_vol_n = 1; + BL_LOG_INFO("%s: slice %d\n", __func__, voln); + status = set_vol_array(&p, end, vols, voln); + break; + case BLOCK_VOLUME_STRIPE: + BLK_READBUF(p, end, 8); + READ_SECTOR(vol->param.bv_stripe_unit); + off_t stripe_unit = vol->param.bv_stripe_unit; + /* Check limitations imposed by device-mapper */ + if ((stripe_unit & (stripe_unit - 1)) != 0 + || stripe_unit < (off_t) (PAGE_SIZE >> 9)) + return -EIO; + BLK_READBUF(p, end, 4); + READ32(vol->bv_vol_n); + if (!vol->bv_vol_n) + return -EIO; + *array_cnt = vol->bv_vol_n; + BL_LOG_INFO("%s: stripe %d nvols=%d unit=%ld\n", __func__, voln, + vol->bv_vol_n, (long)stripe_unit); + status = set_vol_array(&p, end, vols, voln); + if (status) + return status; + for (j = 1; j < vol->bv_vol_n; j++) { + if (vol->bv_vols[j]->bv_size != + vol->bv_vols[0]->bv_size) { + BL_LOG_ERR("varying subvol size\n"); + return -EIO; + } + } + vol->bv_size = vol->bv_vols[0]->bv_size * vol->bv_vol_n; + break; + case BLOCK_VOLUME_CONCAT: + BLK_READBUF(p, end, 4); + READ32(vol->bv_vol_n); + if (!vol->bv_vol_n) + return -EIO; + *array_cnt = vol->bv_vol_n; + BL_LOG_INFO("%s: concat %d %d\n", __func__, voln, + vol->bv_vol_n); + status = set_vol_array(&p, end, vols, voln); + if (status) + return status; + vol->bv_size = sum_subvolume_sizes(vol); + break; + default: + BL_LOG_ERR("Unknown volume type %i\n", vol->bv_type); + out_err: + return -EIO; + } + *pp = p; + return status; +} + +uint64_t process_deviceinfo(const char *dev_addr_buf, + unsigned int dev_addr_len, + uint32_t *major, uint32_t *minor) +{ + int num_vols, i, status, count; + uint32_t *p, *end; + struct bl_volume *vols = NULL, **arrays = NULL, **arrays_ptr = NULL; + uint64_t dev = 0; + + p = (uint32_t *) dev_addr_buf; + end = (uint32_t *) ((char *)p + dev_addr_len); + + /* Decode block volume */ + BLK_READBUF(p, end, 4); + READ32(num_vols); + BL_LOG_INFO("%s: %d vols\n", __func__, num_vols); + if (num_vols <= 0) + goto out_err; + + vols = (struct bl_volume *)malloc(num_vols * sizeof(struct bl_volume)); + if (!vols) { + BL_LOG_ERR("%s: Out of memory\n", __func__); + goto out_err; + } + + /* Each volume in vols array needs its own array. Save time by + * allocating them all in one large hunk. Because each volume + * array can only reference previous volumes, and because once + * a concat or stripe references a volume, it may never be + * referenced again, the volume arrays are guaranteed to fit + * in the suprisingly small space allocated. + */ + arrays_ptr = arrays = + (struct bl_volume **)malloc(num_vols * 2 * + sizeof(struct bl_volume *)); + if (!arrays) { + BL_LOG_ERR("%s: Out of memory\n", __func__); + goto out_err; + } + + for (i = 0; i < num_vols; i++) { + vols[i].bv_vols = arrays_ptr; + status = decode_blk_volume(&p, end, vols, i, &count); + if (status) + goto out_err; + arrays_ptr += count; + } + + if (p != end) { + BL_LOG_ERR("p is not equal to end!\n"); + goto out_err; + } + + dev = dm_device_create(vols, num_vols); + if (dev) { + *major = MAJOR(dev); + *minor = MINOR(dev); + } + + out_err: + if (vols) + free(vols); + if (arrays) + free(arrays); + return dev; +} diff --git a/utils/blkmapd/dm-device.c b/utils/blkmapd/dm-device.c new file mode 100644 index 0000000..0f4f148 --- /dev/null +++ b/utils/blkmapd/dm-device.c @@ -0,0 +1,518 @@ +/* + * dm-device.c: create or remove device via device mapper API. + * + * Copyright (c) 2010 EMC Corporation, Haiying Tang + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "device-discovery.h" + +#define DM_DEV_NAME_LEN 256 + +#ifndef DM_MAX_TYPE_NAME +#define DM_MAX_TYPE_NAME 16 +#endif + +#define DM_PARAMS_LEN 512 /* XXX: is this enough for target? */ +#define TYPE_HAS_DEV(type) ((type == BLOCK_VOLUME_SIMPLE) || \ + (type == BLOCK_VOLUME_PSEUDO)) + +struct bl_dm_table { + uint64_t offset; + uint64_t size; + char target_type[DM_MAX_TYPE_NAME]; + char params[DM_PARAMS_LEN]; + struct bl_dm_table *next; +}; + +struct bl_dm_tree { + uint64_t dev; + struct dm_tree *tree; + struct bl_dm_tree *next; +}; + +static const char dm_name[] = "pnfs_vol_%u"; + +static unsigned int dev_count; + +static inline struct bl_dm_table *bl_dm_table_alloc(void) +{ + return (struct bl_dm_table *)calloc(1, sizeof(struct bl_dm_table)); +} + +static void bl_dm_table_free(struct bl_dm_table *bl_table_head) +{ + struct bl_dm_table *p; + + while (bl_table_head) { + p = bl_table_head->next; + free(bl_table_head); + bl_table_head = p; + } +} + +static void add_to_bl_dm_table(struct bl_dm_table **bl_table_head, + struct bl_dm_table *table) +{ + struct bl_dm_table *p; + + if (!*bl_table_head) { + *bl_table_head = table; + return; + } + p = *bl_table_head; + while (p->next) + p = p->next; + p->next = table; +} + +struct bl_dm_tree *bl_tree_head; + +static struct bl_dm_tree *find_bl_dm_tree(uint64_t dev) +{ + struct bl_dm_tree *p; + + for (p = bl_tree_head; p; p = p->next) { + if (p->dev == dev) + break; + } + return p; +} + +static void del_from_bl_dm_tree(uint64_t dev) +{ + struct bl_dm_tree *p, *pre = bl_tree_head; + + for (p = pre; p; p = p->next) { + if (p->dev == dev) { + pre->next = p->next; + if (p == bl_tree_head) + bl_tree_head = bl_tree_head->next; + free(p); + break; + } + pre = p; + } +} + +static void add_to_bl_dm_tree(struct bl_dm_tree *tree) +{ + struct bl_dm_tree *p; + + if (!bl_tree_head) { + bl_tree_head = tree; + return; + } + p = bl_tree_head; + while (p->next) + p = p->next; + p->next = tree; + return; +} + +/* + * Create device via device mapper + * return 0 when creation failed + * return dev no for created device + */ +static uint64_t +dm_device_create_mapped(const char *dev_name, struct bl_dm_table *p) +{ + struct dm_task *dmt; + struct dm_info dminfo; + int ret = 0; + + dmt = dm_task_create(DM_DEVICE_CREATE); + if (!dmt) { + BL_LOG_ERR("Create dm_task for %s failed\n", dev_name); + return 0; + } + ret = dm_task_set_name(dmt, dev_name); + if (!ret) + goto err_out; + + while (p) { + ret = + dm_task_add_target(dmt, p->offset, p->size, p->target_type, + p->params); + if (!ret) + goto err_out; + p = p->next; + } + + ret = dm_task_run(dmt) && dm_task_get_info(dmt, &dminfo) + && dminfo.exists; + + if (!ret) + goto err_out; + + dm_task_update_nodes(); + + err_out: + dm_task_destroy(dmt); + + if (!ret) { + BL_LOG_ERR("Create device %s failed\n", dev_name); + return 0; + } + return MKDEV(dminfo.major, dminfo.minor); +} + +static int dm_device_remove_byname(const char *dev_name) +{ + struct dm_task *dmt; + int ret = 0; + + BL_LOG_INFO("%s: %s\n", __func__, dev_name); + + dmt = dm_task_create(DM_DEVICE_REMOVE); + if (!dmt) + return 0; + + ret = dm_task_set_name(dmt, dev_name) && dm_task_run(dmt); + + dm_task_update_nodes(); + dm_task_destroy(dmt); + + return ret; +} + +int dm_device_remove(uint64_t dev) +{ + struct dm_task *dmt; + struct dm_names *dmnames; + char *name = NULL; + int ret = 0; + + /* Look for dev_name via dev, if dev_name could be transferred here, + we could jump to DM_DEVICE_REMOVE directly */ + + dmt = dm_task_create(DM_DEVICE_LIST); + if (!dmt) { + BL_LOG_ERR("dm_task creation failed\n"); + goto out; + } + + ret = dm_task_run(dmt); + if (!ret) { + BL_LOG_ERR("dm_task_run failed\n"); + goto out; + } + + dmnames = dm_task_get_names(dmt); + if (!dmnames || !dmnames->dev) { + BL_LOG_ERR("dm_task_get_names failed\n"); + goto out; + } + + while (dmnames) { + if (dmnames->dev == dev) { + name = strdup(dmnames->name); + break; + } + dmnames = (void *)dmnames + dmnames->next; + } + + if (!name) { + BL_LOG_ERR("Could not find device\n"); + goto out; + } + + dm_task_update_nodes(); + + out: + if (dmt) + dm_task_destroy(dmt); + + /* Start to remove device */ + if (name) { + ret = dm_device_remove_byname(name); + free(name); + } + + return ret; +} + +static void dm_devicelist_remove(unsigned int start, unsigned int end) +{ + char dev_name[DM_DEV_NAME_LEN]; + unsigned int count; + + if (start >= dev_count || end <= 1 || start >= end - 1) + return; + + for (count = end - 1; count > start; count--) { + snprintf(dev_name, sizeof dev_name, dm_name, count - 1); + dm_device_remove_byname(dev_name); + } + + return; +} + +static void bl_dm_remove_tree(uint64_t dev) +{ + struct bl_dm_tree *p; + + p = find_bl_dm_tree(dev); + if (!p) + return; + + dm_tree_free(p->tree); + del_from_bl_dm_tree(dev); +} + +static int bl_dm_create_tree(uint64_t dev) +{ + struct dm_tree *tree; + struct bl_dm_tree *bl_tree; + + bl_tree = find_bl_dm_tree(dev); + if (bl_tree) + return 1; + + tree = dm_tree_create(); + if (!tree) + return 0; + + if (!dm_tree_add_dev(tree, MAJOR(dev), MINOR(dev))) { + dm_tree_free(tree); + return 0; + } + + bl_tree = malloc(sizeof(struct bl_dm_tree)); + if (!bl_tree) { + dm_tree_free(tree); + return 0; + } + + bl_tree->dev = dev; + bl_tree->tree = tree; + bl_tree->next = NULL; + add_to_bl_dm_tree(bl_tree); + + return 1; +} + +int dm_device_remove_all(uint64_t *dev) +{ + struct bl_dm_tree *p; + struct dm_tree_node *node; + const char *uuid; + int ret = 0; + uint32_t major, minor; + uint64_t bl_dev; + + memcpy(&major, dev, sizeof(uint32_t)); + memcpy(&minor, (void *)dev + sizeof(uint32_t), sizeof(uint32_t)); + bl_dev = MKDEV(major, minor); + p = find_bl_dm_tree(bl_dev); + if (!p) + return ret; + + node = dm_tree_find_node(p->tree, MAJOR(bl_dev), MINOR(bl_dev)); + if (!node) + return ret; + + uuid = dm_tree_node_get_uuid(node); + if (!uuid) + return ret; + + dm_device_remove(bl_dev); + ret = dm_tree_deactivate_children(node, uuid, strlen(uuid)); + dm_task_update_nodes(); + bl_dm_remove_tree(bl_dev); + + return ret; +} + +static int dm_device_exists(char *dev_name) +{ + char fullname[DM_DEV_NAME_LEN]; + + snprintf(fullname, sizeof fullname, "/dev/mapper/%s", dev_name); + return (access(fullname, F_OK) >= 0); +} + +/* TODO: check the value for DM_DEV_NAME_LEN, DM_TYPE_LEN, DM_PARAMS_LEN */ +uint64_t dm_device_create(struct bl_volume *vols, int num_vols) +{ + uint64_t size, stripe_unit, dev = 0; + unsigned int count = dev_count; + int volnum, i, pos; + struct bl_volume *node; + char *tmp; + struct bl_dm_table *table = NULL; + struct bl_dm_table *bl_table_head = NULL; + unsigned int len; + char *dev_name = NULL; + + /* Create pseudo device here */ + for (volnum = 0; volnum < num_vols; volnum++) { + node = &vols[volnum]; + switch (node->bv_type) { + case BLOCK_VOLUME_SIMPLE: + /* Do not need to create device here */ + dev = node->param.bv_dev; + goto continued; + case BLOCK_VOLUME_SLICE: + table = bl_dm_table_alloc(); + if (!table) + goto out; + table->offset = 0; + table->size = node->bv_size; + strcpy(table->target_type, "linear"); + if (!TYPE_HAS_DEV(node->bv_vols[0]->bv_type)) { + free(table); + goto out; + } + dev = node->bv_vols[0]->param.bv_dev; + tmp = table->params; + if (!dm_format_dev(tmp, DM_PARAMS_LEN, + MAJOR(dev), MINOR(dev))) { + free(table); + goto out; + } + tmp += strlen(tmp); + sprintf(tmp, " %lu", node->param.bv_offset); + add_to_bl_dm_table(&bl_table_head, table); + break; + case BLOCK_VOLUME_STRIPE: + table = bl_dm_table_alloc(); + if (!table) + goto out; + table->offset = 0; + /* Truncate size to a stripe unit boundary */ + stripe_unit = node->param.bv_stripe_unit; + table->size = + node->bv_size - (node->bv_size % stripe_unit); + strcpy(table->target_type, "striped"); + sprintf(table->params, "%d %llu %n", node->bv_vol_n, + (long long unsigned) stripe_unit, &pos); + /* Copy subdev major:minor to params */ + tmp = table->params + pos; + len = DM_PARAMS_LEN - pos; + for (i = 0; i < node->bv_vol_n; i++) { + if (!TYPE_HAS_DEV(node->bv_vols[i]->bv_type)) { + free(table); + goto out; + } + dev = node->bv_vols[i]->param.bv_dev; + if (!dm_format_dev(tmp, len, MAJOR(dev), + MINOR(dev))) { + free(table); + goto out; + } + pos = strlen(tmp); + tmp += pos; + len -= pos; + sprintf(tmp, " %d ", 0); + tmp += 3; + len -= 3; + } + add_to_bl_dm_table(&bl_table_head, table); + break; + case BLOCK_VOLUME_CONCAT: + size = 0; + for (i = 0; i < node->bv_vol_n; i++) { + table = bl_dm_table_alloc(); + if (!table) + goto out; + table->offset = size; + table->size = node->bv_vols[i]->bv_size; + if (!TYPE_HAS_DEV(node->bv_vols[i]->bv_type)) { + free(table); + goto out; + } + strcpy(table->target_type, "linear"); + tmp = table->params; + dev = node->bv_vols[i]->param.bv_dev; + if (!dm_format_dev(tmp, DM_PARAMS_LEN, + MAJOR(dev), MINOR(dev))) { + free(table); + goto out; + } + tmp += strlen(tmp); + sprintf(tmp, " %d", 0); + size += table->size; + add_to_bl_dm_table(&bl_table_head, table); + } + break; + default: + /* Delete previous temporary devices */ + dm_devicelist_remove(count, dev_count); + goto out; + } /* end of swtich */ + /* Create dev_name here. Name of device is pnfs_vol_XXX */ + if (dev_name) + free(dev_name); + dev_name = (char *)calloc(DM_DEV_NAME_LEN, sizeof(char)); + if (!dev_name) { + BL_LOG_ERR("%s: Out of memory\n", __func__); + goto out; + } + do { + snprintf(dev_name, DM_DEV_NAME_LEN, dm_name, + dev_count++); + } while (dm_device_exists(dev_name)); + + dev = dm_device_create_mapped(dev_name, bl_table_head); + BL_LOG_INFO("%s: %d %s %d:%d\n", __func__, volnum, dev_name, + (int) MAJOR(dev), (int) MINOR(dev)); + if (!dev) { + /* Delete previous temporary devices */ + dm_devicelist_remove(count, dev_count); + goto out; + } + node->param.bv_dev = dev; + /* TODO: extend use with PSEUDO later */ + node->bv_type = BLOCK_VOLUME_PSEUDO; + + continued: + if (bl_table_head) + bl_dm_table_free(bl_table_head); + bl_table_head = NULL; + } + out: + if (bl_table_head) { + bl_dm_table_free(bl_table_head); + bl_table_head = NULL; + } + if (dev) + bl_dm_create_tree(dev); + if (dev_name) + free(dev_name); + return dev; +} diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c index b107c7c..7432a65 100644 --- a/utils/exportfs/exportfs.c +++ b/utils/exportfs/exportfs.c @@ -401,7 +401,7 @@ validate_export(nfs_export *exp) int fs_has_fsid = 0; if (stat(path, &stb) < 0) { - xlog(L_ERROR, "Failed to stat %s: %m \n", path); + xlog(L_ERROR, "Failed to stat %s: %m", path); return; } if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) { @@ -448,6 +448,36 @@ is_hostname(const char *sp) return true; } +/* + * Take care to perform an explicit reverse lookup on presentation + * addresses. Otherwise we don't get a real canonical name or a + * complete list of addresses. + */ +static struct addrinfo * +address_list(const char *hostname) +{ + struct addrinfo *ai; + char *cname; + + ai = host_pton(hostname); + if (ai != NULL) { + /* @hostname was a presentation address */ + cname = host_canonname(ai->ai_addr); + freeaddrinfo(ai); + if (cname != NULL) + goto out; + } + /* @hostname was a hostname or had no reverse mapping */ + cname = strdup(hostname); + if (cname == NULL) + return NULL; + +out: + ai = host_addrinfo(cname); + free(cname); + return ai; +} + static int matchhostname(const char *hostname1, const char *hostname2) { @@ -464,10 +494,10 @@ matchhostname(const char *hostname1, const char *hostname2) if (!is_hostname(hostname1) || !is_hostname(hostname2)) return 0; - results1 = host_addrinfo(hostname1); + results1 = address_list(hostname1); if (results1 == NULL) goto out; - results2 = host_addrinfo(hostname2); + results2 = address_list(hostname2); if (results2 == NULL) goto out; @@ -499,9 +529,12 @@ export_d_read(const char *dname) n = scandir(dname, &namelist, NULL, versionsort); - if (n < 0) - xlog(L_NOTICE, "scandir %s: %s\n", dname, strerror(errno)); - else if (n == 0) + if (n < 0) { + if (errno == ENOENT) + /* Silently return */ + return; + xlog(L_NOTICE, "scandir %s: %s", dname, strerror(errno)); + } else if (n == 0) return; for (i = 0; i < n; i++) { @@ -528,7 +561,7 @@ export_d_read(const char *dname) 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); + xlog(L_WARNING, "Too long file name: %s in %s", d->d_name, dname); continue; } @@ -642,7 +675,7 @@ dump(int verbose) static void error(nfs_export *exp, int err) { - xlog(L_ERROR, "%s:%s: %s\n", exp->m_client->m_hostname, + xlog(L_ERROR, "%s:%s: %s", exp->m_client->m_hostname, exp->m_export.e_path, strerror(err)); } diff --git a/utils/exportfs/exports.man b/utils/exportfs/exports.man index b202583..54adfeb 100644 --- a/utils/exportfs/exports.man +++ b/utils/exportfs/exports.man @@ -80,25 +80,25 @@ This is specified by a single character (not to be confused with the .I wildcard entry above) and will match all clients. -'''.TP -'''.B =public -'''This is a special ``hostname'' that identifies the given directory name -'''as the public root directory (see the section on WebNFS in -'''.BR nfsd (8) -'''for a discussion of WebNFS and the public root handle). When using this -'''convention, -'''.B =public -'''must be the only entry on this line, and must have no export options -'''associated with it. Note that this does -'''.I not -'''actually export the named directory; you still have to set the exports -'''options in a separate entry. -'''.PP -'''The public root path can also be specified by invoking -'''.I nfsd -'''with the -'''.B \-\-public\-root -'''option. Multiple specifications of a public root will be ignored. +.\".TP +.\".B =public +.\"This is a special ``hostname'' that identifies the given directory name +.\"as the public root directory (see the section on WebNFS in +.\".BR nfsd (8) +.\"for a discussion of WebNFS and the public root handle). When using this +.\"convention, +.\".B =public +.\"must be the only entry on this line, and must have no export options +.\"associated with it. Note that this does +.\".I not +.\"actually export the named directory; you still have to set the exports +.\"options in a separate entry. +.\".PP +.\"The public root path can also be specified by invoking +.\".I nfsd +.\"with the +.\".B \-\-public\-root +.\"option. Multiple specifications of a public root will be ignored. .PP If a client matches more than one of the specifications above, then the first match from the above list order takes precedence - regardless of @@ -130,7 +130,7 @@ this way are ro, rw, no_root_squash, root_squash, and all_squash. .BR exportfs understands the following export options: .TP -.IR secure "\*d +.IR secure This option requires that requests originate on an Internet port less than IPPORT_RESERVED (1024). This option is on by default. To turn it off, specify @@ -311,24 +311,24 @@ with ACL support (i.e. by default, .I no_acl is off). -'''.TP -'''.I noaccess -'''This makes everything below the directory inaccessible for the named -'''client. This is useful when you want to export a directory hierarchy to -'''a client, but exclude certain subdirectories. The client's view of a -'''directory flagged with noaccess is very limited; it is allowed to read -'''its attributes, and lookup `.' and `..'. These are also the only entries -'''returned by a readdir. -'''.TP -'''.IR link_relative -'''Convert absolute symbolic links (where the link contents start with a -'''slash) into relative links by prepending the necessary number of ../'s -'''to get from the directory containing the link to the root on the -'''server. This has subtle, perhaps questionable, semantics when the file -'''hierarchy is not mounted at its root. -'''.TP -'''.IR link_absolute -'''Leave all symbolic link as they are. This is the default operation. +.\".TP +.\".I noaccess +.\"This makes everything below the directory inaccessible for the named +.\"client. This is useful when you want to export a directory hierarchy to +.\"a client, but exclude certain subdirectories. The client's view of a +.\"directory flagged with noaccess is very limited; it is allowed to read +.\"its attributes, and lookup `.' and `..'. These are also the only entries +.\"returned by a readdir. +.\".TP +.\".IR link_relative +.\"Convert absolute symbolic links (where the link contents start with a +.\"slash) into relative links by prepending the necessary number of ../'s +.\"to get from the directory containing the link to the root on the +.\"server. This has subtle, perhaps questionable, semantics when the file +.\"hierarchy is not mounted at its root. +.\".TP +.\".IR link_absolute +.\"Leave all symbolic link as they are. This is the default operation. .TP .IR mountpoint= path @@ -411,21 +411,21 @@ and can be turned off with .IR no_root_squash . .PP By default, -'''.B nfsd -'''tries to obtain the anonymous uid and gid by looking up user -'''.I nobody -'''in the password file at startup time. If it isn't found, a uid and gid +.\".B nfsd +.\"tries to obtain the anonymous uid and gid by looking up user +.\".I nobody +.\"in the password file at startup time. If it isn't found, a uid and gid .B exportfs chooses a uid and gid of 65534 for squashed access. These values can also be overridden by the .IR anonuid " and " anongid options. -'''.PP -'''In addition to this, -'''.B nfsd -'''lets you specify arbitrary uids and gids that should be mapped to user -'''nobody as well. +.\".PP +.\"In addition to this, +.\".B nfsd +.\"lets you specify arbitrary uids and gids that should be mapped to user +.\"nobody as well. Finally, you can map all user requests to the anonymous uid by specifying the .IR all_squash " option. @@ -490,7 +490,7 @@ The format for extra export tables is the same as /srv/www \-sync,rw server @trusted @external(ro) /foo 2001:db8:9:e54::/64(rw) 192.0.2.0/24(rw) /build buildhost[0-9].local.domain(rw) -'''/pub/private (noaccess) +.\"/pub/private (noaccess) .fi .PP The first line exports the entire filesystem to machines master and trusty. @@ -508,21 +508,21 @@ as well as the `@trusted' netgroup, and read-only to netgroup `@external', all three mounts with the `sync' option enabled. The seventh line exports a directory to both an IPv6 and an IPv4 subnet. The eighth line demonstrates a character class wildcard match. -''' The last line denies all NFS clients -'''access to the private directory. -'''.SH CAVEATS -'''Unlike other NFS server implementations, this -'''.B nfsd -'''allows you to export both a directory and a subdirectory thereof to -'''the same host, for instance -'''.IR /usr " and " /usr/X11R6 . -'''In this case, the mount options of the most specific entry apply. For -'''instance, when a user on the client host accesses a file in -'''.IR /usr/X11R6 , -'''the mount options given in the -'''.I /usr/X11R6 -'''entry apply. This is also true when the latter is a wildcard or netgroup -'''entry. +.\" The last line denies all NFS clients +.\"access to the private directory. +.\".SH CAVEATS +.\"Unlike other NFS server implementations, this +.\".B nfsd +.\"allows you to export both a directory and a subdirectory thereof to +.\"the same host, for instance +.\".IR /usr " and " /usr/X11R6 . +.\"In this case, the mount options of the most specific entry apply. For +.\"instance, when a user on the client host accesses a file in +.\".IR /usr/X11R6 , +.\"the mount options given in the +.\".I /usr/X11R6 +.\"entry apply. This is also true when the latter is a wildcard or netgroup +.\"entry. .SH FILES /etc/exports /etc/exports.d @@ -532,17 +532,17 @@ a character class wildcard match. .BR mountd (8), .BR nfsd (8), .BR showmount (8). -'''.SH DIAGNOSTICS -'''An error parsing the file is reported using syslogd(8) as level NOTICE from -'''a DAEMON whenever -'''.BR nfsd (8) -'''or -'''.BR mountd (8) -'''is started up. Any unknown -'''host is reported at that time, but often not all hosts are not yet known -'''to -'''.BR named (8) -'''at boot time, thus as hosts are found they are reported -'''with the same -'''.BR syslogd (8) -'''parameters. +.\".SH DIAGNOSTICS +.\"An error parsing the file is reported using syslogd(8) as level NOTICE from +.\"a DAEMON whenever +.\".BR nfsd (8) +.\"or +.\".BR mountd (8) +.\"is started up. Any unknown +.\"host is reported at that time, but often not all hosts are not yet known +.\"to +.\".BR named (8) +.\"at boot time, thus as hosts are found they are reported +.\"with the same +.\".BR syslogd (8) +.\"parameters. diff --git a/utils/gssd/context_lucid.c b/utils/gssd/context_lucid.c index b8d4734..3e695ab 100644 --- a/utils/gssd/context_lucid.c +++ b/utils/gssd/context_lucid.c @@ -305,7 +305,7 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf, int32_t *endtime) maj_stat = gss_free_lucid_sec_context(&min_stat, ctx, return_ctx); if (maj_stat != GSS_S_COMPLETE) { - pgsserr("gss_export_lucid_sec_context", + pgsserr("gss_free_lucid_sec_context", maj_stat, min_stat, &krb5oid); printerr(0, "WARN: failed to free lucid sec context\n"); } diff --git a/utils/gssd/svcgssd_krb5.c b/utils/gssd/svcgssd_krb5.c index fc67a6f..6c34faf 100644 --- a/utils/gssd/svcgssd_krb5.c +++ b/utils/gssd/svcgssd_krb5.c @@ -45,6 +45,7 @@ #include "gss_oids.h" #include "err_util.h" #include "svcgssd_krb5.h" +#include "../mount/version.h" #define MYBUFLEN 1024 @@ -169,22 +170,44 @@ 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; + krb5_enctype old_kernel_enctypes[] = { + ENCTYPE_DES_CBC_CRC, + ENCTYPE_DES_CBC_MD5, + ENCTYPE_DES_CBC_MD4 }; + krb5_enctype new_kernel_enctypes[] = { + ENCTYPE_AES256_CTS_HMAC_SHA1_96, + ENCTYPE_AES128_CTS_HMAC_SHA1_96, + ENCTYPE_DES3_CBC_SHA1, + ENCTYPE_ARCFOUR_HMAC, + ENCTYPE_DES_CBC_CRC, + ENCTYPE_DES_CBC_MD5, + ENCTYPE_DES_CBC_MD4 }; + krb5_enctype *default_enctypes, *enctypes; + int default_num_enctypes, num_enctypes; + + + if (linux_version_code() < MAKE_VERSION(2, 6, 35)) { + default_enctypes = old_kernel_enctypes; + default_num_enctypes = + sizeof(old_kernel_enctypes) / sizeof(old_kernel_enctypes[0]); + } else { + default_enctypes = new_kernel_enctypes; + default_num_enctypes = + sizeof(new_kernel_enctypes) / sizeof(new_kernel_enctypes[0]); + } get_kernel_supported_enctypes(); if (parsed_enctypes != NULL) { enctypes = parsed_enctypes; num_enctypes = parsed_num_enctypes; + printerr(2, "%s: Calling gss_set_allowable_enctypes with %d " + "enctypes from the kernel\n", __func__, num_enctypes); } else { enctypes = default_enctypes; num_enctypes = default_num_enctypes; + printerr(2, "%s: Calling gss_set_allowable_enctypes with %d " + "enctypes from defaults\n", __func__, num_enctypes); } maj_stat = gss_set_allowable_enctypes(&min_stat, gssd_creds, diff --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c index 76a56ef..19d9114 100644 --- a/utils/idmapd/idmapd.c +++ b/utils/idmapd/idmapd.c @@ -925,9 +925,9 @@ getfield(char **bpp, char *fld, size_t fldsz) if (*bp == '\\') { if ((n = sscanf(bp, "\\%03o", &val)) != 1) return (-1); - if (val > (char)-1) + if (val > UCHAR_MAX) return (-1); - *fld++ = (char)val; + *fld++ = val; bp += 4; } else { *fld++ = *bp; diff --git a/utils/mount/error.c b/utils/mount/error.c index 1b64bd7..83ad1d2 100644 --- a/utils/mount/error.c +++ b/utils/mount/error.c @@ -16,8 +16,8 @@ * * 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. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA * * To Do: * + Proper support for internationalization diff --git a/utils/mount/error.h b/utils/mount/error.h index 42b28cf..ef80fd0 100644 --- a/utils/mount/error.h +++ b/utils/mount/error.h @@ -16,8 +16,8 @@ * * 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. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA * */ diff --git a/utils/mount/fstab.c b/utils/mount/fstab.c index 1fc9efe..eedbdda 100644 --- a/utils/mount/fstab.c +++ b/utils/mount/fstab.c @@ -86,10 +86,13 @@ mtab_is_writable() { struct mntentchn mounttable; static int got_mtab = 0; +struct mntentchn procmounts; +static int got_procmounts = 0; struct mntentchn fstab; static int got_fstab = 0; static void read_mounttable(void); +static void read_procmounts(void); static void read_fstab(void); static struct mntentchn * @@ -100,6 +103,14 @@ mtab_head(void) return &mounttable; } +static struct mntentchn * +procmounts_head(void) +{ + if (!got_procmounts) + read_procmounts(); + return &procmounts; +} + static struct mntentchn * fstab_head(void) { @@ -186,6 +197,30 @@ read_mounttable() { read_mntentchn(mfp, fnam, mc); } +/* + * Read /proc/mounts. + * This produces a linked list. The list head procmounts is a dummy. + * Return 0 on success. + */ +static void +read_procmounts() { + mntFILE *mfp; + const char *fnam; + struct mntentchn *mc = &procmounts; + + got_procmounts = 1; + mc->nxt = mc->prev = NULL; + + fnam = PROC_MOUNTS; + mfp = nfs_setmntent(fnam, "r"); + if (mfp == NULL || mfp->mntent_fp == NULL) { + nfs_error(_("warning: can't open %s: %s"), + PROC_MOUNTS, strerror (errno)); + return; + } + read_mntentchn(mfp, fnam, mc); +} + static void read_fstab() { @@ -224,6 +259,23 @@ getmntdirbackward (const char *name, struct mntentchn *mcprev) { return NULL; } +/* + * Given the directory name NAME, and the place MCPREV we found it last time, + * try to find more occurrences. + */ +struct mntentchn * +getprocmntdirbackward (const char *name, struct mntentchn *mcprev) { + struct mntentchn *mc, *mc0; + + mc0 = procmounts_head(); + if (!mcprev) + mcprev = mc0; + for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev) + if (streq(mc->m.mnt_dir, name)) + return mc; + return NULL; +} + /* * Given the device name NAME, and the place MCPREV we found it last time, * try to find more occurrences. diff --git a/utils/mount/fstab.h b/utils/mount/fstab.h index dc7c9fc..313bf9b 100644 --- a/utils/mount/fstab.h +++ b/utils/mount/fstab.h @@ -18,6 +18,7 @@ struct mntentchn { struct mntentchn *getmntoptfile (const char *file); struct mntentchn *getmntdirbackward (const char *dir, struct mntentchn *mc); +struct mntentchn *getprocmntdirbackward (const char *name, struct mntentchn *mc); struct mntentchn *getmntdevbackward (const char *dev, struct mntentchn *mc); struct mntentchn *getfsfile (const char *file); diff --git a/utils/mount/mount_libmount.c b/utils/mount/mount_libmount.c index 6dd6484..e450d79 100644 --- a/utils/mount/mount_libmount.c +++ b/utils/mount/mount_libmount.c @@ -15,8 +15,8 @@ * * 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. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA * */ @@ -61,10 +61,19 @@ int nomtab; * 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) +static void store_mount_options(struct libmnt_fs *fs, const char *nfs_opts) { - mnt_fs_set_fs_options(fs, opts); /* for mtab */ - mnt_fs_set_attributes(fs, opts); /* for non-mtab systems */ + char *o = NULL; + + mnt_fs_set_attributes(fs, nfs_opts); /* for non-mtab systems */ + + /* for mtab create a new options list */ + mnt_optstr_append_option(&o, mnt_fs_get_vfs_options(fs), NULL); + mnt_optstr_append_option(&o, nfs_opts, NULL); + mnt_optstr_append_option(&o, mnt_fs_get_user_options(fs), NULL); + + mnt_fs_set_options(fs, o); + free(o); } /* diff --git a/utils/mount/network.c b/utils/mount/network.c index d1f91dc..e7bd522 100644 --- a/utils/mount/network.c +++ b/utils/mount/network.c @@ -16,8 +16,8 @@ * * 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. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA * */ diff --git a/utils/mount/network.h b/utils/mount/network.h index 81c6f22..9c75856 100644 --- a/utils/mount/network.h +++ b/utils/mount/network.h @@ -16,8 +16,8 @@ * * 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. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA * */ diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man index be91a25..ce40933 100644 --- a/utils/mount/nfs.man +++ b/utils/mount/nfs.man @@ -46,11 +46,10 @@ files on this mount point. The fifth and sixth fields on each line are not used by NFS, thus conventionally each contain the digit zero. For example: .P -.SP -.NF -.TA 2.5i +0.75i +0.75i +1.0i +.nf +.ta 8n +14n +14n +9n +20n server:path /mountpoint fstype option,option,... 0 0 -.FI +.fi .P The server's hostname and export pathname are separated by a colon, while @@ -113,12 +112,16 @@ option may mitigate some of the risks of using the option. .TP 1.5i .BI timeo= n -The time (in tenths of a second) the NFS client waits for a -response before it retries an NFS request. If this -option is not specified, requests are retried every -60 seconds for NFS over TCP. -The NFS client does not perform any kind of timeout backoff -for NFS over TCP. +The time in deciseconds (tenths of a second) the NFS client waits for a +response before it retries an NFS request. +.IP +For NFS over TCP the default +.B timeo +value is 600 (60 seconds). +The NFS client performs linear backoff: After each retransmission the +timeout is increased by +.BR timeo +up to the maximum of 600 seconds. .IP However, for NFS over UDP, the client uses an adaptive algorithm to estimate an appropriate timeout value for frequently used @@ -752,8 +755,8 @@ If 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 +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. @@ -900,40 +903,40 @@ The following example from an file causes the mount command to negotiate reasonable defaults for NFS behavior. .P -.NF -.TA 2.5i +0.7i +0.7i +.7i +.nf +.ta 8n +16n +6n +6n +30n server:/export /mnt nfs defaults 0 0 -.FI +.fi .P Here is an example from an /etc/fstab file for an NFS version 2 mount over UDP. .P -.NF -.TA 2.5i +0.7i +0.7i +.7i +.nf +.ta 8n +16n +6n +6n +30n server:/export /mnt nfs nfsvers=2,proto=udp 0 0 -.FI +.fi .P Try this example to mount using NFS version 4 over TCP with Kerberos 5 mutual authentication. .P -.NF -.TA 2.5i +0.7i +0.7i +.7i +.nf +.ta 8n +16n +6n +6n +30n server:/export /mnt nfs4 sec=krb5 0 0 -.FI +.fi .P This example can be used to mount /usr over NFS. .P -.NF -.TA 2.5i +0.7i +0.7i +.7i +.nf +.ta 8n +16n +6n +6n +30n server:/export /usr nfs ro,nolock,nocto,actimeo=3600 0 0 -.FI +.fi .P This example shows how to mount an NFS server using a raw IPv6 link-local address. .P -.NF -.TA 2.5i +0.7i +0.7i +.7i +.nf +.ta 8n +40n +5n +4n +9n [fe80::215:c5ff:fb3e:e2b1%eth0]:/export /mnt nfs defaults 0 0 -.FI +.fi .SH "TRANSPORT METHODS" NFS clients send requests to NFS servers via Remote Procedure Calls, or diff --git a/utils/mount/nfsumount.c b/utils/mount/nfsumount.c index 8cd2852..3538d88 100644 --- a/utils/mount/nfsumount.c +++ b/utils/mount/nfsumount.c @@ -151,65 +151,51 @@ static int del_mtab(const char *spec, const char *node) */ static int nfs_umount_is_vers4(const struct mntentchn *mc) { - char buffer[LINELEN], *next; + struct mntentchn *pmc; + struct mount_options *options; int retval; - FILE *f; - - if ((f = fopen(MOUNTSFILE, "r")) == NULL) { - fprintf(stderr, "%s: %s\n", - MOUNTSFILE, strerror(errno)); - return -1; - } 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) + pmc = getprocmntdirbackward(mc->m.mnt_dir, NULL); + if (!pmc) + goto not_found; + + do { + size_t nlen = strlen(pmc->m.mnt_fsname); + + /* + * It's possible the mount location string in /proc/mounts + * ends with a '/'. In this case, if the entry came from + * /etc/mtab, it won't have the trailing '/' so deal with + * it. + */ + while (pmc->m.mnt_fsname[nlen - 1] == '/') + nlen--; + if (strncmp(pmc->m.mnt_fsname, mc->m.mnt_fsname, nlen) != 0) continue; - type = strtok(NULL, " \t"); - if (type == NULL) - continue; - if (strcmp(type, "nfs4") == 0) + if (strcmp(pmc->m.mnt_type, "nfs4") == 0) goto out_nfs4; - flags = strtok(NULL, " \t"); - if (flags == NULL) - continue; - options = po_split(flags); + options = po_split(pmc->m.mnt_opts); if (options != NULL) { unsigned long version; - int rc; - - rc = nfs_nfs_version(options, &version); + int rc = nfs_nfs_version(options, &version); po_destroy(options); if (rc && version == 4) goto out_nfs4; } - goto out_nfs; - } - if (retval == -1) + if (strcmp(pmc->m.mnt_type, "nfs") == 0) + goto out_nfs; + } while ((pmc = getprocmntdirbackward(mc->m.mnt_dir, pmc)) != NULL); + + if (retval == -1) { +not_found: fprintf(stderr, "%s was not found in %s\n", mc->m.mnt_dir, MOUNTSFILE); - -out: - fclose(f); - return retval; + goto out; + } out_nfs4: if (verbose) @@ -221,7 +207,9 @@ out_nfs: if (verbose) fprintf(stderr, "Legacy NFS mount point detected\n"); retval = 0; - goto out; + +out: + return retval; } static struct option umount_longopts[] = diff --git a/utils/mount/parse_dev.c b/utils/mount/parse_dev.c index c8a58b1..d64b83d 100644 --- a/utils/mount/parse_dev.c +++ b/utils/mount/parse_dev.c @@ -15,8 +15,8 @@ * * 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. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA * */ diff --git a/utils/mount/parse_dev.h b/utils/mount/parse_dev.h index a1288c2..f9857bc 100644 --- a/utils/mount/parse_dev.h +++ b/utils/mount/parse_dev.h @@ -15,8 +15,8 @@ * * 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. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA * */ diff --git a/utils/mount/parse_opt.c b/utils/mount/parse_opt.c index ab869d9..75a0daa 100644 --- a/utils/mount/parse_opt.c +++ b/utils/mount/parse_opt.c @@ -16,8 +16,8 @@ * * 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. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA * */ diff --git a/utils/mount/parse_opt.h b/utils/mount/parse_opt.h index 2c0b5f4..5037207 100644 --- a/utils/mount/parse_opt.h +++ b/utils/mount/parse_opt.h @@ -16,8 +16,8 @@ * * 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. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA * */ diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c index f1aa503..314a806 100644 --- a/utils/mount/stropts.c +++ b/utils/mount/stropts.c @@ -16,8 +16,8 @@ * * 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. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA * */ @@ -437,8 +437,8 @@ static int nfs_construct_new_options(struct mount_options *options, if (po_append(options, new_option) == PO_FAILED) return 0; - po_remove_all(options, "port"); - if (nfs_pmap->pm_port != NFS_PORT) { + if(po_remove_all(options, "port") == PO_FOUND || + nfs_pmap->pm_port != NFS_PORT) { snprintf(new_option, sizeof(new_option) - 1, "port=%lu", nfs_pmap->pm_port); if (po_append(options, new_option) == PO_FAILED) @@ -538,6 +538,8 @@ nfs_rewrite_pmap_mount_options(struct mount_options *options) errno = ESPIPE; if (rpc_createerr.cf_stat == RPC_PROGNOTREGISTERED) errno = EOPNOTSUPP; + else if (rpc_createerr.cf_stat == RPC_AUTHERROR) + errno = EACCES; else if (rpc_createerr.cf_error.re_errno != 0) errno = rpc_createerr.cf_error.re_errno; return 0; diff --git a/utils/mount/stropts.h b/utils/mount/stropts.h index b4fd888..37316eb 100644 --- a/utils/mount/stropts.h +++ b/utils/mount/stropts.h @@ -16,8 +16,8 @@ * * 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. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA * */ diff --git a/utils/mount/token.c b/utils/mount/token.c index 5ef9604..d7e2f4a 100644 --- a/utils/mount/token.c +++ b/utils/mount/token.c @@ -16,8 +16,8 @@ * * 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. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA * */ diff --git a/utils/mount/token.h b/utils/mount/token.h index 5a675ed..17a9c15 100644 --- a/utils/mount/token.h +++ b/utils/mount/token.h @@ -16,8 +16,8 @@ * * 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. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA * */ diff --git a/utils/mount/utils.c b/utils/mount/utils.c index 298db39..2778ed7 100644 --- a/utils/mount/utils.c +++ b/utils/mount/utils.c @@ -13,8 +13,8 @@ * * 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. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA * */ diff --git a/utils/mount/utils.h b/utils/mount/utils.h index 3fcd504..224918a 100644 --- a/utils/mount/utils.h +++ b/utils/mount/utils.h @@ -15,8 +15,8 @@ * * 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. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA * */ diff --git a/utils/mount/version.h b/utils/mount/version.h index af61a6f..d7cf680 100644 --- a/utils/mount/version.h +++ b/utils/mount/version.h @@ -15,16 +15,16 @@ * * 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. + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA * */ #ifndef _NFS_UTILS_MOUNT_VERSION_H #define _NFS_UTILS_MOUNT_VERSION_H -#include -#include +#include +#include #include @@ -37,14 +37,16 @@ static inline unsigned int MAKE_VERSION(unsigned int p, unsigned int q, static inline unsigned int linux_version_code(void) { struct utsname my_utsname; - unsigned int p, q, r; + unsigned int p, q = 0, r = 0; + /* UINT_MAX as backward compatibility code should not be run */ if (uname(&my_utsname)) - return 0; + return UINT_MAX; - p = (unsigned int)atoi(strtok(my_utsname.release, ".")); - q = (unsigned int)atoi(strtok(NULL, ".")); - r = (unsigned int)atoi(strtok(NULL, ".")); + /* UINT_MAX as future versions might not start with an integer */ + if (sscanf(my_utsname.release, "%u.%u.%u", &p, &q, &r) < 1) + return UINT_MAX; + return MAKE_VERSION(p, q, r); } diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c index 035624c..bcf5080 100644 --- a/utils/mountd/mountd.c +++ b/utils/mountd/mountd.c @@ -633,7 +633,7 @@ static void insert_group(struct exportnode *e, char *newname) struct groupnode *g; for (g = e->ex_groups; g; g = g->gr_next) - if (strcmp(g->gr_name, newname)) + if (!strcmp(g->gr_name, newname)) return; g = xmalloc(sizeof(*g)); diff --git a/utils/mountd/mountd.man b/utils/mountd/mountd.man index 016a357..b60dc90 100644 --- a/utils/mountd/mountd.man +++ b/utils/mountd/mountd.man @@ -122,7 +122,10 @@ Ignored (compatibility with unfsd??). Specifies the port number used for RPC listener sockets. If this option is not specified, .B rpc.mountd -chooses a random ephemeral port for each listener socket. +will try to consult +.IR /etc/services , +if gets port succeed, set the same port for all listener socket, +otherwise chooses a random ephemeral port for each listener socket. .IP This option can be used to fix the port value of .BR rpc.mountd 's diff --git a/utils/nfsd/nfsd.c b/utils/nfsd/nfsd.c index e7e1470..8bc5d3a 100644 --- a/utils/nfsd/nfsd.c +++ b/utils/nfsd/nfsd.c @@ -94,7 +94,7 @@ main(int argc, char **argv) char *p, *progname, *port; char *haddr = NULL; int socket_up = 0; - int minorvers4 = NFSD_MAXMINORVERS4; /* nfsv4 minor version */ + int minorvers41 = 0; /* nfsv4 minor version */ unsigned int versbits = NFSCTL_ALLBITS; unsigned int protobits = NFSCTL_ALLBITS; unsigned int proto4 = 0; @@ -154,7 +154,12 @@ main(int argc, char **argv) switch((c = strtol(optarg, &p, 0))) { case 4: if (*p == '.') { - minorvers4 = -atoi(p + 1); + int i = atoi(p+1); + if (i != 1) { + fprintf(stderr, "%s: unsupported minor version\n", optarg); + exit(1); + } + minorvers41 = -1; break; } case 3: @@ -251,7 +256,7 @@ main(int argc, char **argv) * registered with rpcbind. Note that on older kernels w/o the right * interfaces, these are a no-op. */ - nfssvc_setvers(versbits, minorvers4); + nfssvc_setvers(versbits, minorvers41); error = nfssvc_set_sockets(AF_INET, proto4, haddr, port); if (!error) diff --git a/utils/nfsd/nfssvc.c b/utils/nfsd/nfssvc.c index f607214..683008e 100644 --- a/utils/nfsd/nfssvc.c +++ b/utils/nfsd/nfssvc.c @@ -269,7 +269,7 @@ nfssvc_set_sockets(const int family, const unsigned int protobits, } void -nfssvc_setvers(unsigned int ctlbits, int minorvers4) +nfssvc_setvers(unsigned int ctlbits, int minorvers41) { int fd, n, off; char *ptr; @@ -280,11 +280,9 @@ nfssvc_setvers(unsigned int ctlbits, int minorvers4) if (fd < 0) return; - n = minorvers4 >= 0 ? minorvers4 : -minorvers4; - if (n >= NFSD_MINMINORVERS4 && n <= NFSD_MAXMINORVERS4) - off += snprintf(ptr+off, sizeof(buf) - off, "%c4.%d ", - minorvers4 > 0 ? '+' : '-', - n); + if (minorvers41) + off += snprintf(ptr+off, sizeof(buf) - off, "%c4.1", + minorvers41 > 0 ? '+' : '-'); for (n = NFSD_MINVERS; n <= NFSD_MAXVERS; n++) { if (NFSCTL_VERISSET(ctlbits, n)) off += snprintf(ptr+off, sizeof(buf) - off, "+%d ", n); diff --git a/utils/nfsd/nfssvc.h b/utils/nfsd/nfssvc.h index 1a01cec..08de0fe 100644 --- a/utils/nfsd/nfssvc.h +++ b/utils/nfsd/nfssvc.h @@ -16,7 +16,7 @@ * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA * */ diff --git a/utils/nfsidmap/nfsidmap.man b/utils/nfsidmap/nfsidmap.man index 6c1a2d4..2381908 100644 --- a/utils/nfsidmap/nfsidmap.man +++ b/utils/nfsidmap/nfsidmap.man @@ -25,9 +25,9 @@ will need to be modified so 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 +create id_resolver * * /usr/sbin/nfsidmap %k %d 600 .PP -This will direct all nfs_idmap requests to the program +This will direct all id_resolver 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 @@ -48,9 +48,9 @@ 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 +create id_resolver uid:* * /some/other/program %k %d 600 .br -create nfs_idmap * * /usr/sbin/nfsidmap %k %d 600 +create id_resolver * * /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. diff --git a/utils/statd/COPYRIGHT b/utils/statd/COPYRIGHT deleted file mode 100644 index 47ff720..0000000 --- a/utils/statd/COPYRIGHT +++ /dev/null @@ -1,25 +0,0 @@ -rpc.statd -- Network Status Monitor (NSM) protocol daemon for Linux. -Copyright (C) 1995-1999, 2002, 2005 Jeffrey A. Uphoff - -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., 675 Massachusetts Ave, Cambridge, MA 02139, USA. - -Jeffrey A. Uphoff -Orion Multisystems, Inc. -3090 Oakmead Village Drive -Santa Clara, CA 95051 -USA - -Phone: +1-408-844-8481 -Internet: juphoff@users.sourceforge.net diff --git a/utils/statd/hostname.c b/utils/statd/hostname.c index 616a3cb..746ecc7 100644 --- a/utils/statd/hostname.c +++ b/utils/statd/hostname.c @@ -225,6 +225,49 @@ statd_canonical_name(const char *hostname) return strdup(buf); } +/* + * Take care to perform an explicit reverse lookup on presentation + * addresses. Otherwise we don't get a real canonical name or a + * complete list of addresses. + * + * Returns an addrinfo list that has ai_canonname filled in, or + * NULL if some error occurs. Caller must free the returned + * list with freeaddrinfo(3). + */ +__attribute_malloc__ +static struct addrinfo * +statd_canonical_list(const char *hostname) +{ + struct addrinfo hint = { +#ifdef IPV6_SUPPORTED + .ai_family = AF_UNSPEC, +#else /* !IPV6_SUPPORTED */ + .ai_family = AF_INET, +#endif /* !IPV6_SUPPORTED */ + .ai_flags = AI_NUMERICHOST, + .ai_protocol = (int)IPPROTO_UDP, + }; + char buf[NI_MAXHOST]; + struct addrinfo *ai; + + ai = get_addrinfo(hostname, &hint); + if (ai != NULL) { + /* @hostname was a presentation address */ + _Bool result; + result = get_nameinfo(ai->ai_addr, ai->ai_addrlen, + buf, (socklen_t)sizeof(buf)); + freeaddrinfo(ai); + if (result) + goto out; + } + /* @hostname was a hostname or had no reverse mapping */ + strcpy(buf, hostname); + +out: + hint.ai_flags = AI_CANONNAME; + return get_addrinfo(buf, &hint); +} + /** * statd_matchhostname - check if two hostnames are equivalent * @hostname1: C string containing hostname @@ -241,11 +284,6 @@ _Bool statd_matchhostname(const char *hostname1, const char *hostname2) { struct addrinfo *ai1, *ai2, *results1 = NULL, *results2 = NULL; - struct addrinfo hint = { - .ai_family = AF_UNSPEC, - .ai_flags = AI_CANONNAME, - .ai_protocol = (int)IPPROTO_UDP, - }; _Bool result = false; if (strcasecmp(hostname1, hostname2) == 0) { @@ -253,10 +291,10 @@ statd_matchhostname(const char *hostname1, const char *hostname2) goto out; } - results1 = get_addrinfo(hostname1, &hint); + results1 = statd_canonical_list(hostname1); if (results1 == NULL) goto out; - results2 = get_addrinfo(hostname2, &hint); + results2 = statd_canonical_list(hostname2); if (results2 == NULL) goto out; @@ -276,7 +314,8 @@ out: freeaddrinfo(results2); freeaddrinfo(results1); - xlog(D_CALL, "%s: hostnames %s", __func__, + xlog(D_CALL, "%s: hostnames %s and %s %s", __func__, + hostname1, hostname2, (result ? "matched" : "did not match")); return result; } diff --git a/utils/statd/monitor.c b/utils/statd/monitor.c index 325dfd3..286a5e2 100644 --- a/utils/statd/monitor.c +++ b/utils/statd/monitor.c @@ -249,7 +249,7 @@ void load_state(void) count = nsm_load_monitor_list(load_one_host); if (count) - xlog(D_GENERAL, "Loaded %u previously monitored hosts"); + xlog(D_GENERAL, "Loaded %u previously monitored hosts", count); } /* diff --git a/utils/statd/rmtcall.c b/utils/statd/rmtcall.c index 0e52fe2..4ecb03c 100644 --- a/utils/statd/rmtcall.c +++ b/utils/statd/rmtcall.c @@ -85,7 +85,7 @@ statd_get_socket(void) memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; - sin.sin_addr.s_addr = INADDR_ANY; + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if (bindresvport(sockfd, &sin) < 0) { xlog(D_GENERAL, "%s: can't bind to reserved port", diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c index 1f490b0..a3290aa 100644 --- a/utils/statd/sm-notify.c +++ b/utils/statd/sm-notify.c @@ -45,8 +45,9 @@ struct nsm_host { struct nsm_host * next; char * name; - char * mon_name; - char * my_name; + const char * mon_name; + const char * my_name; + char * notify_arg; struct addrinfo *ai; time_t last_used; time_t send_next; @@ -93,6 +94,101 @@ smn_lookup(const char *name) return ai; } +#ifdef HAVE_GETNAMEINFO +static char * +smn_get_hostname(const struct sockaddr *sap, const socklen_t salen, + const char *name) +{ + char buf[NI_MAXHOST]; + int error; + + error = getnameinfo(sap, salen, buf, sizeof(buf), NULL, 0, NI_NAMEREQD); + if (error != 0) { + xlog(L_ERROR, "my_name '%s' is unusable: %s", + name, gai_strerror(error)); + return NULL; + } + return strdup(buf); +} +#else /* !HAVE_GETNAMEINFO */ +static char * +smn_get_hostname(const struct sockaddr *sap, + __attribute__ ((unused)) const socklen_t salen, + const char *name) +{ + const struct sockaddr_in *sin = (const struct sockaddr_in *)(char *)sap; + const struct in_addr *addr = &sin->sin_addr; + struct hostent *hp; + + if (sap->sa_family != AF_INET) { + xlog(L_ERROR, "my_name '%s' is unusable: Bad address family", + name); + return NULL; + } + + hp = gethostbyaddr(addr, (socklen_t)sizeof(addr), AF_INET); + if (hp == NULL) { + xlog(L_ERROR, "my_name '%s' is unusable: %s", + name, hstrerror(h_errno)); + return NULL; + } + return strdup(hp->h_name); +} +#endif /* !HAVE_GETNAMEINFO */ + +/* + * Presentation addresses are converted to their canonical hostnames. + * If the IP address does not map to a hostname, it is an error: + * we never send a presentation address as the argument of SM_NOTIFY. + * + * If "name" is not a presentation address, it is left alone. This + * allows the administrator some flexibility if DNS isn't configured + * exactly how sm-notify prefers it. + * + * Returns NUL-terminated C string containing the result, or NULL + * if the canonical name doesn't exist or cannot be determined. + * The caller must free the result with free(3). + */ +__attribute_malloc__ +static char * +smn_verify_my_name(const char *name) +{ + struct addrinfo *ai = NULL; + struct addrinfo hint = { +#ifdef IPV6_SUPPORTED + .ai_family = AF_UNSPEC, +#else /* !IPV6_SUPPORTED */ + .ai_family = AF_INET, +#endif /* !IPV6_SUPPORTED */ + .ai_flags = AI_NUMERICHOST, + }; + char *retval; + int error; + + error = getaddrinfo(name, NULL, &hint, &ai); + switch (error) { + case 0: + /* @name was a presentation address */ + retval = smn_get_hostname(ai->ai_addr, ai->ai_addrlen, name); + freeaddrinfo(ai); + if (retval == NULL) + return NULL; + break; + case EAI_NONAME: + /* @name was not a presentation address */ + retval = strdup(name); + break; + default: + xlog(L_ERROR, "my_name '%s' is unusable: %s", + name, gai_strerror(error)); + return NULL; + } + + xlog(D_GENERAL, "Canonical name for my_name '%s': %s", + name, retval); + return retval; +} + __attribute_malloc__ static struct nsm_host * smn_alloc_host(const char *hostname, const char *mon_name, @@ -104,14 +200,23 @@ smn_alloc_host(const char *hostname, const char *mon_name, if (host == NULL) goto out_nomem; + /* + * mon_name and my_name are preserved so sm-notify can + * find the right monitor record to remove when it is + * done processing this host. + */ host->name = strdup(hostname); - host->mon_name = strdup(mon_name); - host->my_name = strdup(my_name); + host->mon_name = (const char *)strdup(mon_name); + host->my_name = (const char *)strdup(my_name); + host->notify_arg = strdup(opt_srcaddr != NULL ? + nsm_hostname : my_name); if (host->name == NULL || host->mon_name == NULL || - host->my_name == NULL) { - free(host->my_name); - free(host->mon_name); + host->my_name == NULL || + host->notify_arg == NULL) { + free(host->notify_arg); + free((void *)host->my_name); + free((void *)host->mon_name); free(host->name); free(host); goto out_nomem; @@ -135,8 +240,9 @@ static void smn_forget_host(struct nsm_host *host) nsm_delete_notified_host(host->name, host->mon_name, host->my_name); - free(host->my_name); - free(host->mon_name); + free(host->notify_arg); + free((void *)host->my_name); + free((void *)host->mon_name); free(host->name); if (host->ai) freeaddrinfo(host->ai); @@ -157,7 +263,6 @@ smn_get_host(const char *hostname, return 0; insert_host(host); - xlog(D_GENERAL, "Added host %s to notify list", hostname); return 1; } @@ -395,12 +500,14 @@ usage: fprintf(stderr, exit(1); } - xlog_syslog(1); if (opt_debug) { + xlog_syslog(0); xlog_stderr(1); xlog_config(D_ALL, 1); - } else + } else { + xlog_syslog(1); xlog_stderr(0); + } xlog_open(progname); xlog(L_NOTICE, "Version " VERSION " starting"); @@ -414,32 +521,14 @@ usage: fprintf(stderr, } if (opt_srcaddr != NULL) { - struct addrinfo *ai = NULL; - struct addrinfo hint = { - .ai_family = AF_UNSPEC, - .ai_flags = AI_NUMERICHOST, - }; - - if (getaddrinfo(opt_srcaddr, NULL, &hint, &ai)) - /* not a presentation address - use it */ - strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)); - else { - /* was a presentation address - look it up in - * /etc/hosts, so it can be used for my_name */ - int error; + char *name; - freeaddrinfo(ai); - hint.ai_flags = AI_CANONNAME; - error = getaddrinfo(opt_srcaddr, NULL, &hint, &ai); - if (error != 0) { - xlog(L_ERROR, "Bind address %s is unusable: %s", - opt_srcaddr, gai_strerror(error)); - exit(1); - } - strncpy(nsm_hostname, ai->ai_canonname, - sizeof(nsm_hostname)); - freeaddrinfo(ai); - } + name = smn_verify_my_name(opt_srcaddr); + if (name == NULL) + exit(1); + + strncpy(nsm_hostname, name, sizeof(nsm_hostname)); + free(name); } (void)nsm_retire_monitored_hosts(); @@ -557,8 +646,6 @@ notify(const int sock) static int notify_host(int sock, struct nsm_host *host) { - const char *my_name = (opt_srcaddr != NULL ? - nsm_hostname : host->my_name); struct sockaddr *sap; socklen_t salen; @@ -604,11 +691,30 @@ notify_host(int sock, struct nsm_host *host) host->xid = nsm_xmit_rpcbind(sock, sap, SM_PROG, SM_VERS); else host->xid = nsm_xmit_notify(sock, sap, salen, - SM_PROG, my_name, nsm_state); + SM_PROG, host->notify_arg, nsm_state); return 0; } +static void +smn_defer(struct nsm_host *host) +{ + host->xid = 0; + host->send_next = time(NULL) + NSM_MAX_TIMEOUT; + host->timeout = NSM_MAX_TIMEOUT; + insert_host(host); +} + +static void +smn_schedule(struct nsm_host *host) +{ + host->retries = 0; + host->xid = 0; + host->send_next = time(NULL); + host->timeout = NSM_TIMEOUT; + insert_host(host); +} + /* * Extract the returned port number and set up the SM_NOTIFY call. */ @@ -617,21 +723,16 @@ recv_rpcbind_reply(struct sockaddr *sap, struct nsm_host *host, XDR *xdr) { uint16_t port = nsm_recv_rpcbind(sap->sa_family, xdr); - host->send_next = time(NULL); - host->xid = 0; - if (port == 0) { /* No binding for statd... */ xlog(D_GENERAL, "No statd on host %s", host->name); - host->timeout = NSM_MAX_TIMEOUT; - host->send_next += NSM_MAX_TIMEOUT; + smn_defer(host); } else { + xlog(D_GENERAL, "Processing rpcbind reply for %s (port %u)", + host->name, port); nfs_set_port(sap, port); - if (host->timeout >= NSM_MAX_TIMEOUT / 4) - host->timeout = NSM_MAX_TIMEOUT / 4; + smn_schedule(host); } - - insert_host(host); } /* @@ -644,15 +745,11 @@ recv_rpcbind_reply(struct sockaddr *sap, struct nsm_host *host, XDR *xdr) static void recv_notify_reply(struct nsm_host *host) { - char *dot = strchr(host->my_name, '.'); + char *dot = strchr(host->notify_arg, '.'); if (dot != NULL) { *dot = '\0'; - host->send_next = time(NULL); - host->xid = 0; - if (host->timeout >= NSM_MAX_TIMEOUT / 4) - host->timeout = NSM_MAX_TIMEOUT / 4; - insert_host(host); + smn_schedule(host); } else { xlog(D_GENERAL, "Host %s notified successfully", host->name); smn_forget_host(host); @@ -701,7 +798,7 @@ out: } /* - * Insert host into sorted list + * Insert host into notification list, sorted by next send time */ static void insert_host(struct nsm_host *host) @@ -726,6 +823,7 @@ insert_host(struct nsm_host *host) host->next = *where; *where = host; + xlog(D_GENERAL, "Added host %s to notify list", host->name); } /* diff --git a/utils/statd/start-statd b/utils/statd/start-statd index c7805ee..1b345a5 100644 --- a/utils/statd/start-statd +++ b/utils/statd/start-statd @@ -1,8 +1,8 @@ -#!/bin/sh -p +#!/bin/bash -p # nfsmount calls this script when mounting a filesystem with locking # enabled, but when statd does not seem to be running (based on # /var/run/rpc.statd.pid). -# It should run run statd with whatever flags are apropriate for this +# It should run statd with whatever flags are apropriate for this # site. PATH=/sbin:/usr/sbin exec rpc.statd --no-notify diff --git a/utils/statd/statd.man b/utils/statd/statd.man index b72236c..c3c5354 100644 --- a/utils/statd/statd.man +++ b/utils/statd/statd.man @@ -219,7 +219,10 @@ for details. Specifies the port number used for RPC listener sockets. If this option is not specified, .B rpc.statd -chooses a random ephemeral port for each listener socket. +will try to consult +.IR /etc/services , +if gets port succeed, set the same port for all listener socket, +otherwise chooses a random ephemeral port for each listener socket. .IP This option can be used to fix the port value of its listeners when SM_NOTIFY requests must traverse a firewall between clients and servers. @@ -347,8 +350,7 @@ of the requesting lock manager. TI-RPC is a pre-requisite for supporting NFS on IPv6. If TI-RPC support is built into .BR rpc.statd , -it attempts to start listeners on network transports marked -'visible' in +it attempts to start listeners on network transports marked 'visible' in .IR /etc/netconfig . As long as at least one network transport listener starts successfully, .B rpc.statd