1520 lines
42 KiB
Diff
1520 lines
42 KiB
Diff
|
diff --git a/configure.ac b/configure.ac
|
||
|
index 4ade528..6fbcb97 100644
|
||
|
--- a/configure.ac
|
||
|
+++ b/configure.ac
|
||
|
@@ -335,42 +335,37 @@ AC_CHECK_HEADER(rpc/rpc.h, ,
|
||
|
AC_MSG_ERROR([Header file rpc/rpc.h not found - maybe try building with --enable-tirpc]))
|
||
|
CPPFLAGS="${nfsutils_save_CPPFLAGS}"
|
||
|
|
||
|
+AC_CHECK_HEADER(uuid/uuid.h, ,
|
||
|
+ AC_MSG_ERROR([Cannot find needed header file uuid/uuid.h. Install libuuid-devel]))
|
||
|
+
|
||
|
+dnl check for libevent libraries and headers
|
||
|
+AC_LIBEVENT
|
||
|
+
|
||
|
+dnl Check for sqlite3
|
||
|
+AC_SQLITE3_VERS
|
||
|
+
|
||
|
+case $libsqlite3_cv_is_recent in
|
||
|
+yes) ;;
|
||
|
+unknown)
|
||
|
+ dnl do not fail when cross-compiling
|
||
|
+ AC_MSG_WARN([assuming sqlite is at least v3.3]) ;;
|
||
|
+*)
|
||
|
+ AC_MSG_ERROR([nfsdcld requires sqlite-devel]) ;;
|
||
|
+esac
|
||
|
+
|
||
|
if test "$enable_nfsv4" = yes; then
|
||
|
- dnl check for libevent libraries and headers
|
||
|
- AC_LIBEVENT
|
||
|
|
||
|
dnl check for the keyutils libraries and headers
|
||
|
AC_KEYUTILS
|
||
|
|
||
|
- dnl Check for sqlite3
|
||
|
- AC_SQLITE3_VERS
|
||
|
-
|
||
|
if test "$enable_nfsdcld" = "yes"; then
|
||
|
AC_CHECK_HEADERS([libgen.h sys/inotify.h], ,
|
||
|
AC_MSG_ERROR([Cannot find header needed for nfsdcld]))
|
||
|
-
|
||
|
- case $libsqlite3_cv_is_recent in
|
||
|
- yes) ;;
|
||
|
- unknown)
|
||
|
- dnl do not fail when cross-compiling
|
||
|
- AC_MSG_WARN([assuming sqlite is at least v3.3]) ;;
|
||
|
- *)
|
||
|
- AC_MSG_ERROR([nfsdcld requires sqlite-devel]) ;;
|
||
|
- esac
|
||
|
fi
|
||
|
|
||
|
if test "$enable_nfsdcltrack" = "yes"; then
|
||
|
AC_CHECK_HEADERS([libgen.h sys/inotify.h], ,
|
||
|
AC_MSG_ERROR([Cannot find header needed for nfsdcltrack]))
|
||
|
-
|
||
|
- case $libsqlite3_cv_is_recent in
|
||
|
- yes) ;;
|
||
|
- unknown)
|
||
|
- dnl do not fail when cross-compiling
|
||
|
- AC_MSG_WARN([assuming sqlite is at least v3.3]) ;;
|
||
|
- *)
|
||
|
- AC_MSG_ERROR([nfsdcltrack requires sqlite-devel]) ;;
|
||
|
- esac
|
||
|
fi
|
||
|
|
||
|
else
|
||
|
diff --git a/support/export/cache.c b/support/export/cache.c
|
||
|
index 19bbba5..6c0a44a 100644
|
||
|
--- a/support/export/cache.c
|
||
|
+++ b/support/export/cache.c
|
||
|
@@ -1,10 +1,9 @@
|
||
|
-
|
||
|
/*
|
||
|
* Handle communication with knfsd internal cache
|
||
|
*
|
||
|
* We open /proc/net/rpc/{auth.unix.ip,nfsd.export,nfsd.fh}/channel
|
||
|
* and listen for requests (using my_svc_run)
|
||
|
- *
|
||
|
+ *
|
||
|
*/
|
||
|
|
||
|
#ifdef HAVE_CONFIG_H
|
||
|
@@ -16,6 +15,7 @@
|
||
|
#include <sys/select.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <sys/vfs.h>
|
||
|
+#include <sys/wait.h>
|
||
|
#include <time.h>
|
||
|
#include <netinet/in.h>
|
||
|
#include <arpa/inet.h>
|
||
|
@@ -77,6 +77,7 @@ static bool path_lookup_error(int err)
|
||
|
case ENAMETOOLONG:
|
||
|
case ENOENT:
|
||
|
case ENOTDIR:
|
||
|
+ case EACCES:
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
@@ -758,7 +759,15 @@ static struct addrinfo *lookup_client_addr(char *dom)
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
-static void nfsd_fh(int f)
|
||
|
+#define RETRY_SEC 120
|
||
|
+struct delayed {
|
||
|
+ char *message;
|
||
|
+ time_t last_attempt;
|
||
|
+ int f;
|
||
|
+ struct delayed *next;
|
||
|
+} *delayed;
|
||
|
+
|
||
|
+static int nfsd_handle_fh(int f, char *bp, int blen)
|
||
|
{
|
||
|
/* request are:
|
||
|
* domain fsidtype fsid
|
||
|
@@ -776,21 +785,13 @@ static void nfsd_fh(int f)
|
||
|
nfs_export *exp;
|
||
|
int i;
|
||
|
int dev_missing = 0;
|
||
|
- char buf[RPC_CHAN_BUF_SIZE], *bp;
|
||
|
- int blen;
|
||
|
+ char buf[RPC_CHAN_BUF_SIZE];
|
||
|
int did_uncover = 0;
|
||
|
-
|
||
|
- blen = cache_read(f, buf, sizeof(buf));
|
||
|
- if (blen <= 0 || buf[blen-1] != '\n') return;
|
||
|
- buf[blen-1] = 0;
|
||
|
-
|
||
|
- xlog(D_CALL, "nfsd_fh: inbuf '%s'", buf);
|
||
|
-
|
||
|
- bp = buf;
|
||
|
+ int ret = 0;
|
||
|
|
||
|
dom = malloc(blen);
|
||
|
if (dom == NULL)
|
||
|
- return;
|
||
|
+ return ret;
|
||
|
if (qword_get(&bp, dom, blen) <= 0)
|
||
|
goto out;
|
||
|
if (qword_get_int(&bp, &fsidtype) != 0)
|
||
|
@@ -858,7 +859,8 @@ static void nfsd_fh(int f)
|
||
|
case 0:
|
||
|
continue;
|
||
|
case -1:
|
||
|
- goto out;
|
||
|
+ dev_missing ++;
|
||
|
+ continue;
|
||
|
}
|
||
|
if (is_ipaddr_client(dom)
|
||
|
&& !ipaddr_client_matches(exp, ai))
|
||
|
@@ -891,8 +893,10 @@ static void nfsd_fh(int f)
|
||
|
/* The missing dev could be what we want, so just be
|
||
|
* quiet rather than returning stale yet
|
||
|
*/
|
||
|
- if (dev_missing)
|
||
|
+ if (dev_missing) {
|
||
|
+ ret = 1;
|
||
|
goto out;
|
||
|
+ }
|
||
|
} else if (found->e_mountpoint &&
|
||
|
!is_mountpoint(found->e_mountpoint[0]?
|
||
|
found->e_mountpoint:
|
||
|
@@ -902,7 +906,7 @@ static void nfsd_fh(int f)
|
||
|
xlog(L_WARNING, "%s not exported as %d not a mountpoint",
|
||
|
found->e_path, found->e_mountpoint);
|
||
|
*/
|
||
|
- /* FIXME we need to make sure we re-visit this later */
|
||
|
+ ret = 1;
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
@@ -931,7 +935,68 @@ out:
|
||
|
free(found_path);
|
||
|
nfs_freeaddrinfo(ai);
|
||
|
free(dom);
|
||
|
- xlog(D_CALL, "nfsd_fh: found %p path %s", found, found ? found->e_path : NULL);
|
||
|
+ if (!ret)
|
||
|
+ xlog(D_CALL, "nfsd_fh: found %p path %s",
|
||
|
+ found, found ? found->e_path : NULL);
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
+static void nfsd_fh(int f)
|
||
|
+{
|
||
|
+ struct delayed *d, **dp;
|
||
|
+ char inbuf[RPC_CHAN_BUF_SIZE];
|
||
|
+ int blen;
|
||
|
+
|
||
|
+ blen = cache_read(f, inbuf, sizeof(inbuf));
|
||
|
+ if (blen <= 0 || inbuf[blen-1] != '\n') return;
|
||
|
+ inbuf[blen-1] = 0;
|
||
|
+
|
||
|
+ xlog(D_CALL, "nfsd_fh: inbuf '%s'", inbuf);
|
||
|
+
|
||
|
+ if (nfsd_handle_fh(f, inbuf, blen) == 0)
|
||
|
+ return;
|
||
|
+ /* We don't have a definitive answer to give the kernel.
|
||
|
+ * This is because an export marked "mountpoint" isn't a
|
||
|
+ * mountpoint, or because a stat of a mountpoint fails with
|
||
|
+ * a strange error like ETIMEDOUT as is possible with an
|
||
|
+ * NFS mount marked "softerr" which is being re-exported.
|
||
|
+ *
|
||
|
+ * We cannot tell the kernel to retry, so we have to
|
||
|
+ * retry ourselves.
|
||
|
+ */
|
||
|
+ d = malloc(sizeof(*d));
|
||
|
+
|
||
|
+ if (!d)
|
||
|
+ return;
|
||
|
+ d->message = strndup(inbuf, blen);
|
||
|
+ if (!d->message) {
|
||
|
+ free(d);
|
||
|
+ return;
|
||
|
+ }
|
||
|
+ d->f = f;
|
||
|
+ d->last_attempt = time(NULL);
|
||
|
+ d->next = NULL;
|
||
|
+ dp = &delayed;
|
||
|
+ while (*dp)
|
||
|
+ dp = &(*dp)->next;
|
||
|
+ *dp = d;
|
||
|
+}
|
||
|
+
|
||
|
+static void nfsd_retry_fh(struct delayed *d)
|
||
|
+{
|
||
|
+ struct delayed **dp;
|
||
|
+
|
||
|
+ if (nfsd_handle_fh(d->f, d->message, strlen(d->message)+1) == 0) {
|
||
|
+ free(d->message);
|
||
|
+ free(d);
|
||
|
+ return;
|
||
|
+ }
|
||
|
+ d->last_attempt = time(NULL);
|
||
|
+ d->next = NULL;
|
||
|
+ dp = &delayed;
|
||
|
+ while (*dp)
|
||
|
+ dp = &(*dp)->next;
|
||
|
+ *dp = d;
|
||
|
}
|
||
|
|
||
|
#ifdef HAVE_JUNCTION_SUPPORT
|
||
|
@@ -1510,7 +1575,7 @@ static void nfsd_export(int f)
|
||
|
* This will cause it not to appear in the V4 Pseudo-root
|
||
|
* and so a "mount" of this path will fail, just like with
|
||
|
* V3.
|
||
|
- * And filehandle for this mountpoint from an earlier
|
||
|
+ * Any filehandle for this mountpoint from an earlier
|
||
|
* mount will block in nfsd.fh lookup.
|
||
|
*/
|
||
|
xlog(L_WARNING,
|
||
|
@@ -1600,40 +1665,63 @@ int cache_process_req(fd_set *readfds)
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
- * cache_process_loop - process incoming upcalls
|
||
|
+ * cache_process - process incoming upcalls
|
||
|
+ * Returns -ve on error, or number of fds in svc_fds
|
||
|
+ * that might need processing.
|
||
|
*/
|
||
|
-void cache_process_loop(void)
|
||
|
+int cache_process(fd_set *readfds)
|
||
|
{
|
||
|
- fd_set readfds;
|
||
|
+ fd_set fdset;
|
||
|
int selret;
|
||
|
+ struct timeval tv = { 24*3600, 0 };
|
||
|
|
||
|
- FD_ZERO(&readfds);
|
||
|
-
|
||
|
- for (;;) {
|
||
|
-
|
||
|
- cache_set_fds(&readfds);
|
||
|
- v4clients_set_fds(&readfds);
|
||
|
+ if (!readfds) {
|
||
|
+ FD_ZERO(&fdset);
|
||
|
+ readfds = &fdset;
|
||
|
+ }
|
||
|
+ cache_set_fds(readfds);
|
||
|
+ v4clients_set_fds(readfds);
|
||
|
+
|
||
|
+ if (delayed) {
|
||
|
+ time_t now = time(NULL);
|
||
|
+ time_t delay;
|
||
|
+ if (delayed->last_attempt > now)
|
||
|
+ /* Clock updated - retry immediately */
|
||
|
+ delayed->last_attempt = now - RETRY_SEC;
|
||
|
+ delay = delayed->last_attempt + RETRY_SEC - now;
|
||
|
+ if (delay < 0)
|
||
|
+ delay = 0;
|
||
|
+ tv.tv_sec = delay;
|
||
|
+ }
|
||
|
+ selret = select(FD_SETSIZE, readfds, NULL, NULL, &tv);
|
||
|
|
||
|
- selret = select(FD_SETSIZE, &readfds,
|
||
|
- (void *) 0, (void *) 0, (struct timeval *) 0);
|
||
|
+ if (delayed) {
|
||
|
+ time_t now = time(NULL);
|
||
|
+ struct delayed *d = delayed;
|
||
|
|
||
|
+ if (d->last_attempt + RETRY_SEC <= now) {
|
||
|
+ delayed = d->next;
|
||
|
+ d->next = NULL;
|
||
|
+ nfsd_retry_fh(d);
|
||
|
+ }
|
||
|
+ }
|
||
|
|
||
|
- switch (selret) {
|
||
|
- case -1:
|
||
|
- if (errno == EINTR || errno == ECONNREFUSED
|
||
|
- || errno == ENETUNREACH || errno == EHOSTUNREACH)
|
||
|
- continue;
|
||
|
- xlog(L_ERROR, "my_svc_run() - select: %m");
|
||
|
- return;
|
||
|
+ switch (selret) {
|
||
|
+ case -1:
|
||
|
+ if (errno == EINTR || errno == ECONNREFUSED
|
||
|
+ || errno == ENETUNREACH || errno == EHOSTUNREACH)
|
||
|
+ return 0;
|
||
|
+ return -1;
|
||
|
|
||
|
- default:
|
||
|
- cache_process_req(&readfds);
|
||
|
- v4clients_process(&readfds);
|
||
|
- }
|
||
|
+ default:
|
||
|
+ selret -= cache_process_req(readfds);
|
||
|
+ selret -= v4clients_process(readfds);
|
||
|
+ if (selret < 0)
|
||
|
+ selret = 0;
|
||
|
}
|
||
|
+ return selret;
|
||
|
}
|
||
|
|
||
|
-
|
||
|
/*
|
||
|
* Give IP->domain and domain+path->options to kernel
|
||
|
* % echo nfsd $IP $[now+DEFAULT_TTL] $domain > /proc/net/rpc/auth.unix.ip/channel
|
||
|
@@ -1773,3 +1861,74 @@ cache_get_filehandle(nfs_export *exp, int len, char *p)
|
||
|
fh.fh_size = qword_get(&bp, (char *)fh.fh_handle, NFS3_FHSIZE);
|
||
|
return &fh;
|
||
|
}
|
||
|
+
|
||
|
+/* Wait for all worker child processes to exit and reap them */
|
||
|
+void
|
||
|
+cache_wait_for_workers(char *prog)
|
||
|
+{
|
||
|
+ int status;
|
||
|
+ pid_t pid;
|
||
|
+
|
||
|
+ for (;;) {
|
||
|
+
|
||
|
+ pid = waitpid(0, &status, 0);
|
||
|
+
|
||
|
+ if (pid < 0) {
|
||
|
+ if (errno == ECHILD)
|
||
|
+ return; /* no more children */
|
||
|
+ xlog(L_FATAL, "%s: can't wait: %s\n", prog,
|
||
|
+ strerror(errno));
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Note: because we SIG_IGN'd SIGCHLD earlier, this
|
||
|
+ * does not happen on 2.6 kernels, and waitpid() blocks
|
||
|
+ * until all the children are dead then returns with
|
||
|
+ * -ECHILD. But, we don't need to do anything on the
|
||
|
+ * death of individual workers, so we don't care. */
|
||
|
+ xlog(L_NOTICE, "%s: reaped child %d, status %d\n",
|
||
|
+ prog, (int)pid, status);
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+/* Fork num_threads worker children and wait for them */
|
||
|
+int
|
||
|
+cache_fork_workers(char *prog, int num_threads)
|
||
|
+{
|
||
|
+ int i;
|
||
|
+ pid_t pid;
|
||
|
+
|
||
|
+ if (num_threads <= 1)
|
||
|
+ return 1;
|
||
|
+
|
||
|
+ xlog(L_NOTICE, "%s: starting %d threads\n", prog, num_threads);
|
||
|
+
|
||
|
+ for (i = 0 ; i < num_threads ; i++) {
|
||
|
+ pid = fork();
|
||
|
+ if (pid < 0) {
|
||
|
+ xlog(L_FATAL, "%s: cannot fork: %s\n", prog,
|
||
|
+ strerror(errno));
|
||
|
+ }
|
||
|
+ if (pid == 0) {
|
||
|
+ /* worker child */
|
||
|
+
|
||
|
+ /* Re-enable the default action on SIGTERM et al
|
||
|
+ * so that workers die naturally when sent them.
|
||
|
+ * Only the parent unregisters with pmap and
|
||
|
+ * hence needs to do special SIGTERM handling. */
|
||
|
+ struct sigaction sa;
|
||
|
+ sa.sa_handler = SIG_DFL;
|
||
|
+ sa.sa_flags = 0;
|
||
|
+ sigemptyset(&sa.sa_mask);
|
||
|
+ sigaction(SIGHUP, &sa, NULL);
|
||
|
+ sigaction(SIGINT, &sa, NULL);
|
||
|
+ sigaction(SIGTERM, &sa, NULL);
|
||
|
+
|
||
|
+ /* fall into my_svc_run in caller */
|
||
|
+ return 1;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ /* in parent */
|
||
|
+ cache_wait_for_workers(prog);
|
||
|
+ return 0;
|
||
|
+}
|
||
|
diff --git a/support/export/export.h b/support/export/export.h
|
||
|
index 8d5a0d3..e2009cc 100644
|
||
|
--- a/support/export/export.h
|
||
|
+++ b/support/export/export.h
|
||
|
@@ -29,6 +29,9 @@ int v4clients_process(fd_set *fdset);
|
||
|
struct nfs_fh_len *
|
||
|
cache_get_filehandle(nfs_export *exp, int len, char *p);
|
||
|
int cache_export(nfs_export *exp, char *path);
|
||
|
+int cache_fork_workers(char *prog, int num_threads);
|
||
|
+void cache_wait_for_workers(char *prog);
|
||
|
+int cache_process(fd_set *readfds);
|
||
|
|
||
|
bool ipaddr_client_matches(nfs_export *exp, struct addrinfo *ai);
|
||
|
bool namelist_client_matches(nfs_export *exp, char *dom);
|
||
|
diff --git a/support/include/version.h b/support/include/version.h
|
||
|
deleted file mode 120000
|
||
|
index b7db0bb..0000000
|
||
|
--- a/support/include/version.h
|
||
|
+++ /dev/null
|
||
|
@@ -1 +0,0 @@
|
||
|
-../../utils/mount/version.h
|
||
|
\ No newline at end of file
|
||
|
diff --git a/support/include/version.h b/support/include/version.h
|
||
|
new file mode 100644
|
||
|
index 0000000..d7cf680
|
||
|
--- /dev/null
|
||
|
+++ b/support/include/version.h
|
||
|
@@ -0,0 +1,53 @@
|
||
|
+/*
|
||
|
+ * version.h -- get running kernel version
|
||
|
+ *
|
||
|
+ * Copyright (C) 2008 Oracle. All rights reserved.
|
||
|
+ *
|
||
|
+ * This program is free software; you can redistribute it and/or
|
||
|
+ * modify it under the terms of the GNU General Public
|
||
|
+ * License as published by the Free Software Foundation; either
|
||
|
+ * version 2 of the License, or (at your option) any later version.
|
||
|
+ *
|
||
|
+ * This program is distributed in the hope that it will be useful,
|
||
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
+ * General Public License for more details.
|
||
|
+ *
|
||
|
+ * You should have received a copy of the GNU General Public
|
||
|
+ * License along with this program; if not, write to the
|
||
|
+ * Free Software Foundation, Inc.,
|
||
|
+ * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA
|
||
|
+ *
|
||
|
+ */
|
||
|
+
|
||
|
+#ifndef _NFS_UTILS_MOUNT_VERSION_H
|
||
|
+#define _NFS_UTILS_MOUNT_VERSION_H
|
||
|
+
|
||
|
+#include <stdio.h>
|
||
|
+#include <limits.h>
|
||
|
+
|
||
|
+#include <sys/utsname.h>
|
||
|
+
|
||
|
+static inline unsigned int MAKE_VERSION(unsigned int p, unsigned int q,
|
||
|
+ unsigned int r)
|
||
|
+{
|
||
|
+ return (65536 * p) + (256 * q) + r;
|
||
|
+}
|
||
|
+
|
||
|
+static inline unsigned int linux_version_code(void)
|
||
|
+{
|
||
|
+ struct utsname my_utsname;
|
||
|
+ unsigned int p, q = 0, r = 0;
|
||
|
+
|
||
|
+ /* UINT_MAX as backward compatibility code should not be run */
|
||
|
+ if (uname(&my_utsname))
|
||
|
+ return UINT_MAX;
|
||
|
+
|
||
|
+ /* 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);
|
||
|
+}
|
||
|
+
|
||
|
+#endif /* _NFS_UTILS_MOUNT_VERSION_H */
|
||
|
diff --git a/support/junction/junction.c b/support/junction/junction.c
|
||
|
index 0628bb0..c1ec8ff 100644
|
||
|
--- a/support/junction/junction.c
|
||
|
+++ b/support/junction/junction.c
|
||
|
@@ -63,7 +63,7 @@ junction_open_path(const char *pathname, int *fd)
|
||
|
if (pathname == NULL || fd == NULL)
|
||
|
return FEDFS_ERR_INVAL;
|
||
|
|
||
|
- tmp = open(pathname, O_PATH|O_DIRECTORY);
|
||
|
+ tmp = open(pathname, O_DIRECTORY);
|
||
|
if (tmp == -1) {
|
||
|
switch (errno) {
|
||
|
case EPERM:
|
||
|
diff --git a/support/nfsidmap/libnfsidmap.c b/support/nfsidmap/libnfsidmap.c
|
||
|
index 0a912e5..f8c3648 100644
|
||
|
--- a/support/nfsidmap/libnfsidmap.c
|
||
|
+++ b/support/nfsidmap/libnfsidmap.c
|
||
|
@@ -219,10 +219,15 @@ static int domain_from_dns(char **domain)
|
||
|
|
||
|
if (gethostname(hname, sizeof(hname)) == -1)
|
||
|
return -1;
|
||
|
- if ((he = gethostbyname(hname)) == NULL)
|
||
|
- return -1;
|
||
|
- if ((c = strchr(he->h_name, '.')) == NULL || *++c == '\0')
|
||
|
- return -1;
|
||
|
+ if ((he = gethostbyname(hname)) == NULL) {
|
||
|
+ IDMAP_LOG(1, ("libnfsidmap: DNS lookup of hostname failed. Attempting to use domain from hostname as is."));
|
||
|
+ if ((c = strchr(hname, '.')) == NULL || *++c == '\0')
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+ else {
|
||
|
+ if ((c = strchr(he->h_name, '.')) == NULL || *++c == '\0')
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
/*
|
||
|
* Query DNS to see if the _nfsv4idmapdomain TXT record exists
|
||
|
* If so use it...
|
||
|
@@ -387,7 +392,7 @@ int nfs4_init_name_mapping(char *conffile)
|
||
|
dflt = 1;
|
||
|
ret = domain_from_dns(&default_domain);
|
||
|
if (ret) {
|
||
|
- IDMAP_LOG(1, ("libnfsidmap: Unable to determine "
|
||
|
+ IDMAP_LOG(0, ("libnfsidmap: Unable to determine "
|
||
|
"the NFSv4 domain; Using '%s' as the NFSv4 domain "
|
||
|
"which means UIDs will be mapped to the 'Nobody-User' "
|
||
|
"user defined in %s",
|
||
|
diff --git a/support/reexport/fsidd.c b/support/reexport/fsidd.c
|
||
|
index 410b3a3..d4b245e 100644
|
||
|
--- a/support/reexport/fsidd.c
|
||
|
+++ b/support/reexport/fsidd.c
|
||
|
@@ -3,7 +3,9 @@
|
||
|
#endif
|
||
|
|
||
|
#include <assert.h>
|
||
|
+#ifdef HAVE_DLFCN_H
|
||
|
#include <dlfcn.h>
|
||
|
+#endif
|
||
|
#include <event2/event.h>
|
||
|
#include <limits.h>
|
||
|
#include <stdint.h>
|
||
|
@@ -18,14 +20,18 @@
|
||
|
|
||
|
#include "conffile.h"
|
||
|
#include "reexport_backend.h"
|
||
|
+#include "reexport.h"
|
||
|
#include "xcommon.h"
|
||
|
#include "xlog.h"
|
||
|
|
||
|
-#define FSID_SOCKET_NAME "fsid.sock"
|
||
|
-
|
||
|
static struct event_base *evbase;
|
||
|
static struct reexpdb_backend_plugin *dbbackend = &sqlite_plug_ops;
|
||
|
|
||
|
+/* assert_safe() always evalutes it argument, as it might have
|
||
|
+ * a side-effect. assert() won't if compiled with NDEBUG
|
||
|
+ */
|
||
|
+#define assert_safe(__sideeffect) (__sideeffect ? 0 : ({assert(0) ; 0;}))
|
||
|
+
|
||
|
static void client_cb(evutil_socket_t cl, short ev, void *d)
|
||
|
{
|
||
|
struct event *me = d;
|
||
|
@@ -56,12 +62,11 @@ static void client_cb(evutil_socket_t cl, short ev, void *d)
|
||
|
|
||
|
if (dbbackend->fsidnum_by_path(req_path, &fsidnum, false, &found)) {
|
||
|
if (found)
|
||
|
- assert(asprintf(&answer, "+ %u", fsidnum) != -1);
|
||
|
+ assert_safe(asprintf(&answer, "+ %u", fsidnum) != -1);
|
||
|
else
|
||
|
- assert(asprintf(&answer, "+ ") != -1);
|
||
|
-
|
||
|
+ assert_safe(asprintf(&answer, "+ ") != -1);
|
||
|
} else {
|
||
|
- assert(asprintf(&answer, "- %s", "Command failed") != -1);
|
||
|
+ assert_safe(asprintf(&answer, "- %s", "Command failed") != -1);
|
||
|
}
|
||
|
|
||
|
(void)send(cl, answer, strlen(answer), 0);
|
||
|
@@ -78,13 +83,13 @@ static void client_cb(evutil_socket_t cl, short ev, void *d)
|
||
|
|
||
|
if (dbbackend->fsidnum_by_path(req_path, &fsidnum, true, &found)) {
|
||
|
if (found) {
|
||
|
- assert(asprintf(&answer, "+ %u", fsidnum) != -1);
|
||
|
+ assert_safe(asprintf(&answer, "+ %u", fsidnum) != -1);
|
||
|
} else {
|
||
|
- assert(asprintf(&answer, "+ ") != -1);
|
||
|
+ assert_safe(asprintf(&answer, "+ ") != -1);
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
- assert(asprintf(&answer, "- %s", "Command failed") != -1);
|
||
|
+ assert_safe(asprintf(&answer, "- %s", "Command failed") != -1);
|
||
|
}
|
||
|
|
||
|
(void)send(cl, answer, strlen(answer), 0);
|
||
|
@@ -106,15 +111,15 @@ static void client_cb(evutil_socket_t cl, short ev, void *d)
|
||
|
}
|
||
|
|
||
|
if (bad_input) {
|
||
|
- assert(asprintf(&answer, "- %s", "Command failed: Bad input") != -1);
|
||
|
+ assert_safe(asprintf(&answer, "- %s", "Command failed: Bad input") != -1);
|
||
|
} else {
|
||
|
if (dbbackend->path_by_fsidnum(fsidnum, &path, &found)) {
|
||
|
if (found)
|
||
|
- assert(asprintf(&answer, "+ %s", path) != -1);
|
||
|
+ assert_safe(asprintf(&answer, "+ %s", path) != -1);
|
||
|
else
|
||
|
- assert(asprintf(&answer, "+ ") != -1);
|
||
|
+ assert_safe(asprintf(&answer, "+ ") != -1);
|
||
|
} else {
|
||
|
- assert(asprintf(&answer, "+ ") != -1);
|
||
|
+ assert_safe(asprintf(&answer, "+ ") != -1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@@ -129,7 +134,7 @@ static void client_cb(evutil_socket_t cl, short ev, void *d)
|
||
|
} else {
|
||
|
char *answer = NULL;
|
||
|
|
||
|
- assert(asprintf(&answer, "- bad command") != -1);
|
||
|
+ assert_safe(asprintf(&answer, "- bad command") != -1);
|
||
|
(void)send(cl, answer, strlen(answer), 0);
|
||
|
|
||
|
free(answer);
|
||
|
@@ -163,11 +168,14 @@ int main(void)
|
||
|
|
||
|
sock_file = conf_get_str_with_def("reexport", "fsidd_socket", FSID_SOCKET_NAME);
|
||
|
|
||
|
- unlink(sock_file);
|
||
|
-
|
||
|
memset(&addr, 0, sizeof(struct sockaddr_un));
|
||
|
addr.sun_family = AF_UNIX;
|
||
|
strncpy(addr.sun_path, sock_file, sizeof(addr.sun_path) - 1);
|
||
|
+ if (addr.sun_path[0] == '@')
|
||
|
+ /* "abstract" socket namespace */
|
||
|
+ addr.sun_path[0] = 0;
|
||
|
+ else
|
||
|
+ unlink(sock_file);
|
||
|
|
||
|
srv = socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK, 0);
|
||
|
if (srv == -1) {
|
||
|
diff --git a/support/reexport/reexport.c b/support/reexport/reexport.c
|
||
|
index eddc9bf..d9a700a 100644
|
||
|
--- a/support/reexport/reexport.c
|
||
|
+++ b/support/reexport/reexport.c
|
||
|
@@ -2,7 +2,9 @@
|
||
|
#include <config.h>
|
||
|
#endif
|
||
|
|
||
|
+#ifdef HAVE_DLFCN_H
|
||
|
#include <dlfcn.h>
|
||
|
+#endif
|
||
|
#include <stdint.h>
|
||
|
#include <stdio.h>
|
||
|
#include <sys/random.h>
|
||
|
@@ -38,6 +40,9 @@ static bool connect_fsid_service(void)
|
||
|
memset(&addr, 0, sizeof(struct sockaddr_un));
|
||
|
addr.sun_family = AF_UNIX;
|
||
|
strncpy(addr.sun_path, sock_file, sizeof(addr.sun_path) - 1);
|
||
|
+ if (addr.sun_path[0] == '@')
|
||
|
+ /* "abstract" socket namespace */
|
||
|
+ addr.sun_path[0] = 0;
|
||
|
|
||
|
s = socket(AF_UNIX, SOCK_SEQPACKET, 0);
|
||
|
if (s == -1) {
|
||
|
diff --git a/support/reexport/reexport.h b/support/reexport/reexport.h
|
||
|
index 3bed03a..85fd59c 100644
|
||
|
--- a/support/reexport/reexport.h
|
||
|
+++ b/support/reexport/reexport.h
|
||
|
@@ -1,6 +1,8 @@
|
||
|
#ifndef REEXPORT_H
|
||
|
#define REEXPORT_H
|
||
|
|
||
|
+#include "nfslib.h"
|
||
|
+
|
||
|
enum {
|
||
|
REEXP_NONE = 0,
|
||
|
REEXP_AUTO_FSIDNUM,
|
||
|
@@ -13,6 +15,6 @@ int reexpdb_fsidnum_by_path(char *path, uint32_t *fsidnum, int may_create);
|
||
|
int reexpdb_apply_reexport_settings(struct exportent *ep, char *flname, int flline);
|
||
|
void reexpdb_uncover_subvolume(uint32_t fsidnum);
|
||
|
|
||
|
-#define FSID_SOCKET_NAME "fsid.sock"
|
||
|
+#define FSID_SOCKET_NAME "@/run/fsid.sock"
|
||
|
|
||
|
#endif /* REEXPORT_H */
|
||
|
diff --git a/systemd/nfs-idmapd.service b/systemd/nfs-idmapd.service
|
||
|
index f38fe52..198ca87 100644
|
||
|
--- a/systemd/nfs-idmapd.service
|
||
|
+++ b/systemd/nfs-idmapd.service
|
||
|
@@ -2,7 +2,8 @@
|
||
|
Description=NFSv4 ID-name mapping service
|
||
|
DefaultDependencies=no
|
||
|
Requires=rpc_pipefs.target
|
||
|
-After=rpc_pipefs.target local-fs.target
|
||
|
+After=rpc_pipefs.target local-fs.target network-online.target
|
||
|
+Wants=network-online.target
|
||
|
|
||
|
BindsTo=nfs-server.service
|
||
|
|
||
|
diff --git a/systemd/nfs.conf.man b/systemd/nfs.conf.man
|
||
|
index bfd3380..866939a 100644
|
||
|
--- a/systemd/nfs.conf.man
|
||
|
+++ b/systemd/nfs.conf.man
|
||
|
@@ -137,8 +137,9 @@ but on the server, this will resolve to the path
|
||
|
.TP
|
||
|
.B exportd
|
||
|
Recognized values:
|
||
|
+.BR manage-gids ,
|
||
|
.BR threads ,
|
||
|
-.BR cache-use-upaddr ,
|
||
|
+.BR cache-use-ipaddr ,
|
||
|
.BR ttl ,
|
||
|
.BR state-directory-path
|
||
|
|
||
|
@@ -204,7 +205,7 @@ Recognized values:
|
||
|
.BR port ,
|
||
|
.BR threads ,
|
||
|
.BR reverse-lookup ,
|
||
|
-.BR cache-use-upaddr ,
|
||
|
+.BR cache-use-ipaddr ,
|
||
|
.BR ttl ,
|
||
|
.BR state-directory-path ,
|
||
|
.BR ha-callout .
|
||
|
diff --git a/utils/exportd/exportd.c b/utils/exportd/exportd.c
|
||
|
index 2dd12cb..a2e370a 100644
|
||
|
--- a/utils/exportd/exportd.c
|
||
|
+++ b/utils/exportd/exportd.c
|
||
|
@@ -16,7 +16,6 @@
|
||
|
#include <string.h>
|
||
|
#include <getopt.h>
|
||
|
#include <errno.h>
|
||
|
-#include <wait.h>
|
||
|
|
||
|
#include "nfslib.h"
|
||
|
#include "conffile.h"
|
||
|
@@ -54,90 +53,19 @@ static char shortopts[] = "d:fghs:t:liT:";
|
||
|
*/
|
||
|
inline static void set_signals(void);
|
||
|
|
||
|
-/* Wait for all worker child processes to exit and reap them */
|
||
|
-static void
|
||
|
-wait_for_workers (void)
|
||
|
-{
|
||
|
- int status;
|
||
|
- pid_t pid;
|
||
|
-
|
||
|
- for (;;) {
|
||
|
-
|
||
|
- pid = waitpid(0, &status, 0);
|
||
|
-
|
||
|
- if (pid < 0) {
|
||
|
- if (errno == ECHILD)
|
||
|
- return; /* no more children */
|
||
|
- xlog(L_FATAL, "mountd: can't wait: %s\n",
|
||
|
- strerror(errno));
|
||
|
- }
|
||
|
-
|
||
|
- /* Note: because we SIG_IGN'd SIGCHLD earlier, this
|
||
|
- * does not happen on 2.6 kernels, and waitpid() blocks
|
||
|
- * until all the children are dead then returns with
|
||
|
- * -ECHILD. But, we don't need to do anything on the
|
||
|
- * death of individual workers, so we don't care. */
|
||
|
- xlog(L_NOTICE, "mountd: reaped child %d, status %d\n",
|
||
|
- (int)pid, status);
|
||
|
- }
|
||
|
-}
|
||
|
-
|
||
|
inline void
|
||
|
cleanup_lockfiles (void)
|
||
|
{
|
||
|
unlink(etab.lockfn);
|
||
|
}
|
||
|
|
||
|
-/* Fork num_threads worker children and wait for them */
|
||
|
static void
|
||
|
-fork_workers(void)
|
||
|
-{
|
||
|
- int i;
|
||
|
- pid_t pid;
|
||
|
-
|
||
|
- xlog(L_NOTICE, "mountd: starting %d threads\n", num_threads);
|
||
|
-
|
||
|
- for (i = 0 ; i < num_threads ; i++) {
|
||
|
- pid = fork();
|
||
|
- if (pid < 0) {
|
||
|
- xlog(L_FATAL, "mountd: cannot fork: %s\n",
|
||
|
- strerror(errno));
|
||
|
- }
|
||
|
- if (pid == 0) {
|
||
|
- /* worker child */
|
||
|
-
|
||
|
- /* Re-enable the default action on SIGTERM et al
|
||
|
- * so that workers die naturally when sent them.
|
||
|
- * Only the parent unregisters with pmap and
|
||
|
- * hence needs to do special SIGTERM handling. */
|
||
|
- struct sigaction sa;
|
||
|
- sa.sa_handler = SIG_DFL;
|
||
|
- sa.sa_flags = 0;
|
||
|
- sigemptyset(&sa.sa_mask);
|
||
|
- sigaction(SIGHUP, &sa, NULL);
|
||
|
- sigaction(SIGINT, &sa, NULL);
|
||
|
- sigaction(SIGTERM, &sa, NULL);
|
||
|
-
|
||
|
- /* fall into my_svc_run in caller */
|
||
|
- return;
|
||
|
- }
|
||
|
- }
|
||
|
-
|
||
|
- /* in parent */
|
||
|
- wait_for_workers();
|
||
|
- cleanup_lockfiles();
|
||
|
- free_state_path_names(&etab);
|
||
|
- xlog(L_NOTICE, "exportd: no more workers, exiting\n");
|
||
|
- exit(0);
|
||
|
-}
|
||
|
-
|
||
|
-static void
|
||
|
killer (int sig)
|
||
|
{
|
||
|
if (num_threads > 1) {
|
||
|
/* play Kronos and eat our children */
|
||
|
kill(0, SIGTERM);
|
||
|
- wait_for_workers();
|
||
|
+ cache_wait_for_workers("exportd");
|
||
|
}
|
||
|
cleanup_lockfiles();
|
||
|
free_state_path_names(&etab);
|
||
|
@@ -145,6 +73,7 @@ killer (int sig)
|
||
|
|
||
|
exit(0);
|
||
|
}
|
||
|
+
|
||
|
static void
|
||
|
sig_hup (int UNUSED(sig))
|
||
|
{
|
||
|
@@ -152,8 +81,9 @@ sig_hup (int UNUSED(sig))
|
||
|
xlog (L_NOTICE, "Received SIGHUP... Ignoring.\n");
|
||
|
return;
|
||
|
}
|
||
|
-inline static void
|
||
|
-set_signals(void)
|
||
|
+
|
||
|
+inline static void
|
||
|
+set_signals(void)
|
||
|
{
|
||
|
struct sigaction sa;
|
||
|
|
||
|
@@ -289,18 +219,27 @@ main(int argc, char **argv)
|
||
|
else if (num_threads > MAX_THREADS)
|
||
|
num_threads = MAX_THREADS;
|
||
|
|
||
|
- if (num_threads > 1)
|
||
|
- fork_workers();
|
||
|
+ /* Open cache channel files BEFORE forking so each upcall is
|
||
|
+ * only handled by one thread. Kernel provides locking for both
|
||
|
+ * read and write.
|
||
|
+ */
|
||
|
+ cache_open();
|
||
|
|
||
|
+ if (cache_fork_workers(progname, num_threads) == 0) {
|
||
|
+ /* We forked, waited, and now need to clean up */
|
||
|
+ cleanup_lockfiles();
|
||
|
+ free_state_path_names(&etab);
|
||
|
+ xlog(L_NOTICE, "%s: no more workers, exiting\n", progname);
|
||
|
+ exit(0);
|
||
|
+ }
|
||
|
|
||
|
- /* Open files now to avoid sharing descriptors among forked processes */
|
||
|
- cache_open();
|
||
|
v4clients_init();
|
||
|
|
||
|
/* Process incoming upcalls */
|
||
|
- cache_process_loop();
|
||
|
+ while (cache_process(NULL) >= 0)
|
||
|
+ ;
|
||
|
|
||
|
- xlog(L_ERROR, "%s: process loop terminated unexpectedly. Exiting...\n",
|
||
|
+ xlog(L_ERROR, "%s: process loop terminated unexpectedly(%m). Exiting...\n",
|
||
|
progname);
|
||
|
|
||
|
free_state_path_names(&etab);
|
||
|
diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c
|
||
|
index 833d8e0..ca9b326 100644
|
||
|
--- a/utils/gssd/gssd.c
|
||
|
+++ b/utils/gssd/gssd.c
|
||
|
@@ -365,6 +365,12 @@ gssd_read_service_info(int dirfd, struct clnt_info *clp)
|
||
|
|
||
|
fail:
|
||
|
printerr(0, "ERROR: failed to parse %s/info\n", clp->relpath);
|
||
|
+ clp->upcall_address = strdup(address);
|
||
|
+ clp->upcall_port = strdup(port);
|
||
|
+ clp->upcall_program = program;
|
||
|
+ clp->upcall_vers = version;
|
||
|
+ clp->upcall_protoname = strdup(protoname);
|
||
|
+ clp->upcall_service = strdup(service);
|
||
|
free(servername);
|
||
|
free(protoname);
|
||
|
clp->servicename = NULL;
|
||
|
@@ -408,6 +414,16 @@ gssd_free_client(struct clnt_info *clp)
|
||
|
free(clp->servicename);
|
||
|
free(clp->servername);
|
||
|
free(clp->protocol);
|
||
|
+ if (!clp->servername) {
|
||
|
+ if (clp->upcall_address)
|
||
|
+ free(clp->upcall_address);
|
||
|
+ if (clp->upcall_port)
|
||
|
+ free(clp->upcall_port);
|
||
|
+ if (clp->upcall_protoname)
|
||
|
+ free(clp->upcall_protoname);
|
||
|
+ if (clp->upcall_service)
|
||
|
+ free(clp->upcall_service);
|
||
|
+ }
|
||
|
free(clp);
|
||
|
}
|
||
|
|
||
|
@@ -446,6 +462,31 @@ gssd_clnt_gssd_cb(int UNUSED(fd), short UNUSED(which), void *data)
|
||
|
{
|
||
|
struct clnt_info *clp = data;
|
||
|
|
||
|
+ /* if there was a failure to translate IP to name for this server,
|
||
|
+ * try again
|
||
|
+ */
|
||
|
+ if (!clp->servername) {
|
||
|
+ if (!gssd_addrstr_to_sockaddr((struct sockaddr *)&clp->addr,
|
||
|
+ clp->upcall_address, clp->upcall_port ?
|
||
|
+ clp->upcall_port : "")) {
|
||
|
+ goto do_upcall;
|
||
|
+ }
|
||
|
+ clp->servername = gssd_get_servername(clp->upcall_address,
|
||
|
+ (struct sockaddr *)&clp->addr, clp->upcall_address);
|
||
|
+ if (!clp->servername)
|
||
|
+ goto do_upcall;
|
||
|
+
|
||
|
+ if (asprintf(&clp->servicename, "%s@%s", clp->upcall_service,
|
||
|
+ clp->servername) < 0) {
|
||
|
+ free(clp->servername);
|
||
|
+ clp->servername = NULL;
|
||
|
+ goto do_upcall;
|
||
|
+ }
|
||
|
+ clp->prog = clp->upcall_program;
|
||
|
+ clp->vers = clp->upcall_vers;
|
||
|
+ clp->protocol = strdup(clp->upcall_protoname);
|
||
|
+ }
|
||
|
+do_upcall:
|
||
|
handle_gssd_upcall(clp);
|
||
|
}
|
||
|
|
||
|
diff --git a/utils/gssd/gssd.h b/utils/gssd/gssd.h
|
||
|
index 519dc43..4e070ed 100644
|
||
|
--- a/utils/gssd/gssd.h
|
||
|
+++ b/utils/gssd/gssd.h
|
||
|
@@ -86,6 +86,12 @@ struct clnt_info {
|
||
|
int gssd_fd;
|
||
|
struct event *gssd_ev;
|
||
|
struct sockaddr_storage addr;
|
||
|
+ char *upcall_address;
|
||
|
+ char *upcall_port;
|
||
|
+ int upcall_program;
|
||
|
+ int upcall_vers;
|
||
|
+ char *upcall_protoname;
|
||
|
+ char *upcall_service;
|
||
|
};
|
||
|
|
||
|
struct clnt_upcall_info {
|
||
|
diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c
|
||
|
index ae568f1..a96647d 100644
|
||
|
--- a/utils/gssd/gssd_proc.c
|
||
|
+++ b/utils/gssd/gssd_proc.c
|
||
|
@@ -412,13 +412,29 @@ create_auth_rpc_client(struct clnt_info *clp,
|
||
|
tid, tgtname);
|
||
|
auth = authgss_create_default(rpc_clnt, tgtname, &sec);
|
||
|
if (!auth) {
|
||
|
+ if (sec.minor_status == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
|
||
|
+ printerr(2, "WARNING: server=%s failed context "
|
||
|
+ "creation with KRB5_AP_ERR_BAD_INTEGRITY\n",
|
||
|
+ clp->servername);
|
||
|
+ if (cred == GSS_C_NO_CREDENTIAL)
|
||
|
+ retval = gssd_refresh_krb5_machine_credential(clp->servername,
|
||
|
+ "*", NULL, 1);
|
||
|
+ else
|
||
|
+ retval = gssd_k5_remove_bad_service_cred(clp->servername);
|
||
|
+ if (!retval) {
|
||
|
+ auth = authgss_create_default(rpc_clnt, tgtname,
|
||
|
+ &sec);
|
||
|
+ if (auth)
|
||
|
+ goto success;
|
||
|
+ }
|
||
|
+ }
|
||
|
/* Our caller should print appropriate message */
|
||
|
printerr(2, "WARNING: Failed to create krb5 context for "
|
||
|
"user with uid %d for server %s\n",
|
||
|
uid, tgtname);
|
||
|
goto out_fail;
|
||
|
}
|
||
|
-
|
||
|
+success:
|
||
|
/* Success !!! */
|
||
|
rpc_clnt->cl_auth = auth;
|
||
|
*clnt_return = rpc_clnt;
|
||
|
@@ -571,7 +587,7 @@ krb5_use_machine_creds(struct clnt_info *clp, uid_t uid,
|
||
|
|
||
|
do {
|
||
|
gssd_refresh_krb5_machine_credential(clp->servername,
|
||
|
- service, srchost);
|
||
|
+ service, srchost, 0);
|
||
|
/*
|
||
|
* Get a list of credential cache names and try each
|
||
|
* of them until one works or we've tried them all
|
||
|
diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
|
||
|
index e3f270e..6f66ef4 100644
|
||
|
--- a/utils/gssd/krb5_util.c
|
||
|
+++ b/utils/gssd/krb5_util.c
|
||
|
@@ -165,7 +165,7 @@ static int select_krb5_ccache(const struct dirent *d);
|
||
|
static int gssd_find_existing_krb5_ccache(uid_t uid, char *dirname,
|
||
|
const char **cctype, struct dirent **d);
|
||
|
static int gssd_get_single_krb5_cred(krb5_context context,
|
||
|
- krb5_keytab kt, struct gssd_k5_kt_princ *ple);
|
||
|
+ krb5_keytab kt, struct gssd_k5_kt_princ *ple, int force_renew);
|
||
|
static int query_krb5_ccache(const char* cred_cache, char **ret_princname,
|
||
|
char **ret_realm);
|
||
|
|
||
|
@@ -391,7 +391,8 @@ gssd_check_if_cc_exists(struct gssd_k5_kt_princ *ple)
|
||
|
static int
|
||
|
gssd_get_single_krb5_cred(krb5_context context,
|
||
|
krb5_keytab kt,
|
||
|
- struct gssd_k5_kt_princ *ple)
|
||
|
+ struct gssd_k5_kt_princ *ple,
|
||
|
+ int force_renew)
|
||
|
{
|
||
|
#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS
|
||
|
krb5_get_init_creds_opt *init_opts = NULL;
|
||
|
@@ -421,7 +422,7 @@ gssd_get_single_krb5_cred(krb5_context context,
|
||
|
*/
|
||
|
now += 300;
|
||
|
pthread_mutex_lock(&ple_lock);
|
||
|
- if (ple->ccname && ple->endtime > now && !nocache) {
|
||
|
+ if (ple->ccname && ple->endtime > now && !nocache && !force_renew) {
|
||
|
printerr(3, "%s(0x%lx): Credentials in CC '%s' are good until %s",
|
||
|
__func__, tid, ple->ccname, ctime((time_t *)&ple->endtime));
|
||
|
code = 0;
|
||
|
@@ -1155,7 +1156,8 @@ err_cache:
|
||
|
static int
|
||
|
gssd_refresh_krb5_machine_credential_internal(char *hostname,
|
||
|
struct gssd_k5_kt_princ *ple,
|
||
|
- char *service, char *srchost)
|
||
|
+ char *service, char *srchost,
|
||
|
+ int force_renew)
|
||
|
{
|
||
|
krb5_error_code code = 0;
|
||
|
krb5_context context;
|
||
|
@@ -1221,7 +1223,7 @@ gssd_refresh_krb5_machine_credential_internal(char *hostname,
|
||
|
goto out_free_kt;
|
||
|
}
|
||
|
}
|
||
|
- retval = gssd_get_single_krb5_cred(context, kt, ple);
|
||
|
+ retval = gssd_get_single_krb5_cred(context, kt, ple, force_renew);
|
||
|
out_free_kt:
|
||
|
krb5_kt_close(context, kt);
|
||
|
out_free_context:
|
||
|
@@ -1344,7 +1346,7 @@ gssd_get_krb5_machine_cred_list(char ***list)
|
||
|
pthread_mutex_unlock(&ple_lock);
|
||
|
/* Make sure cred is up-to-date before returning it */
|
||
|
retval = gssd_refresh_krb5_machine_credential_internal(NULL, ple,
|
||
|
- NULL, NULL);
|
||
|
+ NULL, NULL, 0);
|
||
|
pthread_mutex_lock(&ple_lock);
|
||
|
if (gssd_k5_kt_princ_list == NULL) {
|
||
|
/* Looks like we did shutdown... abort */
|
||
|
@@ -1456,10 +1458,12 @@ gssd_destroy_krb5_principals(int destroy_machine_creds)
|
||
|
*/
|
||
|
int
|
||
|
gssd_refresh_krb5_machine_credential(char *hostname,
|
||
|
- char *service, char *srchost)
|
||
|
+ char *service, char *srchost,
|
||
|
+ int force_renew)
|
||
|
{
|
||
|
return gssd_refresh_krb5_machine_credential_internal(hostname, NULL,
|
||
|
- service, srchost);
|
||
|
+ service, srchost,
|
||
|
+ force_renew);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
@@ -1549,6 +1553,48 @@ gssd_acquire_user_cred(gss_cred_id_t *gss_cred)
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
+/* Removed a service ticket for nfs/<name> from the ticket cache
|
||
|
+ */
|
||
|
+int
|
||
|
+gssd_k5_remove_bad_service_cred(char *name)
|
||
|
+{
|
||
|
+ krb5_creds in_creds, out_creds;
|
||
|
+ krb5_error_code ret;
|
||
|
+ krb5_context context;
|
||
|
+ krb5_ccache cache;
|
||
|
+ krb5_principal principal;
|
||
|
+ int retflags = KRB5_TC_MATCH_SRV_NAMEONLY;
|
||
|
+ char srvname[1024];
|
||
|
+
|
||
|
+ ret = krb5_init_context(&context);
|
||
|
+ if (ret)
|
||
|
+ goto out_cred;
|
||
|
+ ret = krb5_cc_default(context, &cache);
|
||
|
+ if (ret)
|
||
|
+ goto out_free_context;
|
||
|
+ ret = krb5_cc_get_principal(context, cache, &principal);
|
||
|
+ if (ret)
|
||
|
+ goto out_close_cache;
|
||
|
+ memset(&in_creds, 0, sizeof(in_creds));
|
||
|
+ in_creds.client = principal;
|
||
|
+ sprintf(srvname, "nfs/%s", name);
|
||
|
+ ret = krb5_parse_name(context, srvname, &in_creds.server);
|
||
|
+ if (ret)
|
||
|
+ goto out_free_principal;
|
||
|
+ ret = krb5_cc_retrieve_cred(context, cache, retflags, &in_creds, &out_creds);
|
||
|
+ if (ret)
|
||
|
+ goto out_free_principal;
|
||
|
+ ret = krb5_cc_remove_cred(context, cache, 0, &out_creds);
|
||
|
+out_free_principal:
|
||
|
+ krb5_free_principal(context, principal);
|
||
|
+out_close_cache:
|
||
|
+ krb5_cc_close(context, cache);
|
||
|
+out_free_context:
|
||
|
+ krb5_free_context(context);
|
||
|
+out_cred:
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
|
||
|
/*
|
||
|
* this routine obtains a credentials handle via gss_acquire_cred()
|
||
|
diff --git a/utils/gssd/krb5_util.h b/utils/gssd/krb5_util.h
|
||
|
index 2415205..7ef8701 100644
|
||
|
--- a/utils/gssd/krb5_util.h
|
||
|
+++ b/utils/gssd/krb5_util.h
|
||
|
@@ -16,11 +16,13 @@ int gssd_get_krb5_machine_cred_list(char ***list);
|
||
|
void gssd_free_krb5_machine_cred_list(char **list);
|
||
|
void gssd_destroy_krb5_principals(int destroy_machine_creds);
|
||
|
int gssd_refresh_krb5_machine_credential(char *hostname,
|
||
|
- char *service, char *srchost);
|
||
|
+ char *service, char *srchost,
|
||
|
+ int force_renew);
|
||
|
char *gssd_k5_err_msg(krb5_context context, krb5_error_code code);
|
||
|
void gssd_k5_get_default_realm(char **def_realm);
|
||
|
|
||
|
int gssd_acquire_user_cred(gss_cred_id_t *gss_cred);
|
||
|
+int gssd_k5_remove_bad_service_cred(char *srvname);
|
||
|
|
||
|
#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
|
||
|
extern int limit_to_legacy_enctypes;
|
||
|
diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man
|
||
|
index 7a41042..c0ba4d0 100644
|
||
|
--- a/utils/mount/nfs.man
|
||
|
+++ b/utils/mount/nfs.man
|
||
|
@@ -94,31 +94,38 @@ This option is an alternative to the
|
||
|
option.
|
||
|
It is included for compatibility with other operating systems
|
||
|
.TP 1.5i
|
||
|
-.BR soft " / " hard
|
||
|
+.BR soft " / " softerr " / " hard
|
||
|
Determines the recovery behavior of the NFS client
|
||
|
after an NFS request times out.
|
||
|
-If neither option is specified (or if the
|
||
|
+If no option is specified (or if the
|
||
|
.B hard
|
||
|
option is specified), NFS requests are retried indefinitely.
|
||
|
-If the
|
||
|
-.B soft
|
||
|
+If either the
|
||
|
+.BR soft " or " softerr
|
||
|
option is specified, then the NFS client fails an NFS request
|
||
|
after
|
||
|
.B retrans
|
||
|
retransmissions have been sent,
|
||
|
-causing the NFS client to return an error
|
||
|
-to the calling application.
|
||
|
+causing the NFS client to return either the error
|
||
|
+.B EIO
|
||
|
+(for the
|
||
|
+.B soft
|
||
|
+option) or
|
||
|
+.B ETIMEDOUT
|
||
|
+(for the
|
||
|
+.B softerr
|
||
|
+option) to the calling application.
|
||
|
.IP
|
||
|
.I NB:
|
||
|
A so-called "soft" timeout can cause
|
||
|
silent data corruption in certain cases. As such, use the
|
||
|
-.B soft
|
||
|
+.BR soft " or " softerr
|
||
|
option only when client responsiveness
|
||
|
is more important than data integrity.
|
||
|
Using NFS over TCP or increasing the value of the
|
||
|
.B retrans
|
||
|
option may mitigate some of the risks of using the
|
||
|
-.B soft
|
||
|
+.BR soft " or " softerr
|
||
|
option.
|
||
|
.TP 1.5i
|
||
|
.BR softreval " / " nosoftreval
|
||
|
@@ -416,19 +423,6 @@ Note that the
|
||
|
option may also be used by some pNFS drivers to decide how many
|
||
|
connections to set up to the data servers.
|
||
|
.TP 1.5i
|
||
|
-.BR max_connect= n
|
||
|
-While
|
||
|
-.BR nconnect
|
||
|
-option sets a limit on the number of connections that can be established
|
||
|
-to a given server IP,
|
||
|
-.BR max_connect
|
||
|
-option allows the user to specify maximum number of connections to different
|
||
|
-server IPs that belong to the same NFSv4.1+ server (session trunkable
|
||
|
-connections) up to a limit of 16. When client discovers that it established
|
||
|
-a client ID to an already existing server, instead of dropping the newly
|
||
|
-created network transport, the client will add this new connection to the
|
||
|
-list of available transports for that RPC client.
|
||
|
-.TP 1.5i
|
||
|
.BR rdirplus " / " nordirplus
|
||
|
Selects whether to use NFS v3 or v4 READDIRPLUS requests.
|
||
|
If this option is not specified, the NFS client uses READDIRPLUS requests
|
||
|
@@ -590,21 +584,28 @@ If
|
||
|
is specified,
|
||
|
transport layer security is forced off, even if the NFS server supports
|
||
|
transport layer security.
|
||
|
+.IP
|
||
|
If
|
||
|
.B tls
|
||
|
is specified, the client uses RPC-with-TLS to provide in-transit
|
||
|
confidentiality.
|
||
|
+.IP
|
||
|
If
|
||
|
.B mtls
|
||
|
is specified, the client uses RPC-with-TLS to authenticate itself and
|
||
|
to provide in-transit confidentiality.
|
||
|
-If the server does not support RPC-with-TLS or peer authentication
|
||
|
-fails, the mount attempt fails.
|
||
|
+.IP
|
||
|
+If either
|
||
|
+.B tls
|
||
|
+or
|
||
|
+.B mtls
|
||
|
+is specified and the server does not support RPC-with-TLS or peer
|
||
|
+authentication fails, the mount attempt fails.
|
||
|
.IP
|
||
|
If the
|
||
|
.B xprtsec=
|
||
|
option is not specified,
|
||
|
-the default behavior depends on the kernel,
|
||
|
+the default behavior depends on the kernel version,
|
||
|
but is usually equivalent to
|
||
|
.BR "xprtsec=none" .
|
||
|
.SS "Options for NFS versions 2 and 3 only"
|
||
|
@@ -971,6 +972,32 @@ when it identifies itself via a traditional identification string.
|
||
|
.IP
|
||
|
This mount option has no effect with NFSv4 minor versions newer than zero,
|
||
|
which always use TSM-compatible client identification strings.
|
||
|
+.TP 1.5i
|
||
|
+.BR max_connect= n
|
||
|
+While
|
||
|
+.BR nconnect
|
||
|
+option sets a limit on the number of connections that can be established
|
||
|
+to a given server IP,
|
||
|
+.BR max_connect
|
||
|
+option allows the user to specify maximum number of connections to different
|
||
|
+server IPs that belong to the same NFSv4.1+ server (session trunkable
|
||
|
+connections) up to a limit of 16. When client discovers that it established
|
||
|
+a client ID to an already existing server, instead of dropping the newly
|
||
|
+created network transport, the client will add this new connection to the
|
||
|
+list of available transports for that RPC client.
|
||
|
+.TP 1.5i
|
||
|
+.BR trunkdiscovery " / " notrunkdiscovery
|
||
|
+When the client discovers a new filesystem on a NFSv4.1+ server, the
|
||
|
+.BR trunkdiscovery
|
||
|
+mount option will cause it to send a GETATTR for the fs_locations attribute.
|
||
|
+If is receives a non-zero length reply, it will iterate through the response,
|
||
|
+and for each server location it will establish a connection, send an
|
||
|
+EXCHANGE_ID, and test for session trunking. If the trunking test succeeds,
|
||
|
+the connection will be added to the existing set of transports for the server,
|
||
|
+subject to the limit specified by the
|
||
|
+.BR max_connect
|
||
|
+option. The default is
|
||
|
+.BR notrunkdiscovery .
|
||
|
.SH nfs4 FILE SYSTEM TYPE
|
||
|
The
|
||
|
.BR nfs4
|
||
|
@@ -986,7 +1013,6 @@ file. See
|
||
|
.BR nfsmount.conf(5)
|
||
|
for details.
|
||
|
.SH EXAMPLES
|
||
|
-mount option.
|
||
|
To mount using NFS version 3,
|
||
|
use the
|
||
|
.B nfs
|
||
|
diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
|
||
|
index bcf749f..dbd5546 100644
|
||
|
--- a/utils/mountd/mountd.c
|
||
|
+++ b/utils/mountd/mountd.c
|
||
|
@@ -21,7 +21,6 @@
|
||
|
#include <errno.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <sys/resource.h>
|
||
|
-#include <sys/wait.h>
|
||
|
|
||
|
#include "conffile.h"
|
||
|
#include "xmalloc.h"
|
||
|
@@ -119,90 +118,17 @@ cleanup_lockfiles (void)
|
||
|
unlink(rmtab.lockfn);
|
||
|
}
|
||
|
|
||
|
-/* Wait for all worker child processes to exit and reap them */
|
||
|
-static void
|
||
|
-wait_for_workers (void)
|
||
|
-{
|
||
|
- int status;
|
||
|
- pid_t pid;
|
||
|
-
|
||
|
- for (;;) {
|
||
|
-
|
||
|
- pid = waitpid(0, &status, 0);
|
||
|
-
|
||
|
- if (pid < 0) {
|
||
|
- if (errno == ECHILD)
|
||
|
- return; /* no more children */
|
||
|
- xlog(L_FATAL, "mountd: can't wait: %s\n",
|
||
|
- strerror(errno));
|
||
|
- }
|
||
|
-
|
||
|
- /* Note: because we SIG_IGN'd SIGCHLD earlier, this
|
||
|
- * does not happen on 2.6 kernels, and waitpid() blocks
|
||
|
- * until all the children are dead then returns with
|
||
|
- * -ECHILD. But, we don't need to do anything on the
|
||
|
- * death of individual workers, so we don't care. */
|
||
|
- xlog(L_NOTICE, "mountd: reaped child %d, status %d\n",
|
||
|
- (int)pid, status);
|
||
|
- }
|
||
|
-}
|
||
|
-
|
||
|
-/* Fork num_threads worker children and wait for them */
|
||
|
-static void
|
||
|
-fork_workers(void)
|
||
|
-{
|
||
|
- int i;
|
||
|
- pid_t pid;
|
||
|
-
|
||
|
- xlog(L_NOTICE, "mountd: starting %d threads\n", num_threads);
|
||
|
-
|
||
|
- for (i = 0 ; i < num_threads ; i++) {
|
||
|
- pid = fork();
|
||
|
- if (pid < 0) {
|
||
|
- xlog(L_FATAL, "mountd: cannot fork: %s\n",
|
||
|
- strerror(errno));
|
||
|
- }
|
||
|
- if (pid == 0) {
|
||
|
- /* worker child */
|
||
|
-
|
||
|
- /* Re-enable the default action on SIGTERM et al
|
||
|
- * so that workers die naturally when sent them.
|
||
|
- * Only the parent unregisters with pmap and
|
||
|
- * hence needs to do special SIGTERM handling. */
|
||
|
- struct sigaction sa;
|
||
|
- sa.sa_handler = SIG_DFL;
|
||
|
- sa.sa_flags = 0;
|
||
|
- sigemptyset(&sa.sa_mask);
|
||
|
- sigaction(SIGHUP, &sa, NULL);
|
||
|
- sigaction(SIGINT, &sa, NULL);
|
||
|
- sigaction(SIGTERM, &sa, NULL);
|
||
|
-
|
||
|
- /* fall into my_svc_run in caller */
|
||
|
- return;
|
||
|
- }
|
||
|
- }
|
||
|
-
|
||
|
- /* in parent */
|
||
|
- wait_for_workers();
|
||
|
- unregister_services();
|
||
|
- cleanup_lockfiles();
|
||
|
- free_state_path_names(&etab);
|
||
|
- free_state_path_names(&rmtab);
|
||
|
- xlog(L_NOTICE, "mountd: no more workers, exiting\n");
|
||
|
- exit(0);
|
||
|
-}
|
||
|
-
|
||
|
/*
|
||
|
* Signal handler.
|
||
|
*/
|
||
|
-static void
|
||
|
+static void
|
||
|
killer (int sig)
|
||
|
{
|
||
|
unregister_services();
|
||
|
if (num_threads > 1) {
|
||
|
/* play Kronos and eat our children */
|
||
|
kill(0, SIGTERM);
|
||
|
- wait_for_workers();
|
||
|
+ cache_wait_for_workers("mountd");
|
||
|
}
|
||
|
cleanup_lockfiles();
|
||
|
free_state_path_names(&etab);
|
||
|
@@ -220,7 +146,7 @@ sig_hup (int UNUSED(sig))
|
||
|
}
|
||
|
|
||
|
bool_t
|
||
|
-mount_null_1_svc(struct svc_req *rqstp, void *UNUSED(argp),
|
||
|
+mount_null_1_svc(struct svc_req *rqstp, void *UNUSED(argp),
|
||
|
void *UNUSED(resp))
|
||
|
{
|
||
|
struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
|
||
|
@@ -916,12 +842,23 @@ main(int argc, char **argv)
|
||
|
else if (num_threads > MAX_THREADS)
|
||
|
num_threads = MAX_THREADS;
|
||
|
|
||
|
- if (num_threads > 1)
|
||
|
- fork_workers();
|
||
|
+ /* Open cache channel files BEFORE forking so each upcall is
|
||
|
+ * only handled by one thread. Kernel provides locking for both
|
||
|
+ * read and write.
|
||
|
+ */
|
||
|
+ cache_open();
|
||
|
+
|
||
|
+ if (cache_fork_workers("mountd", num_threads) == 0) {
|
||
|
+ /* We forked, waited, and now need to clean up */
|
||
|
+ unregister_services();
|
||
|
+ cleanup_lockfiles();
|
||
|
+ free_state_path_names(&etab);
|
||
|
+ free_state_path_names(&rmtab);
|
||
|
+ xlog(L_NOTICE, "mountd: no more workers, exiting\n");
|
||
|
+ exit(0);
|
||
|
+ }
|
||
|
|
||
|
nfsd_path_init();
|
||
|
- /* Open files now to avoid sharing descriptors among forked processes */
|
||
|
- cache_open();
|
||
|
v4clients_init();
|
||
|
|
||
|
xlog(L_NOTICE, "Version " VERSION " starting");
|
||
|
diff --git a/utils/mountd/mountd.h b/utils/mountd/mountd.h
|
||
|
index d307753..bd5c957 100644
|
||
|
--- a/utils/mountd/mountd.h
|
||
|
+++ b/utils/mountd/mountd.h
|
||
|
@@ -51,13 +51,4 @@ void mountlist_del(char *host, const char *path);
|
||
|
void mountlist_del_all(const struct sockaddr *sap);
|
||
|
mountlist mountlist_list(void);
|
||
|
|
||
|
-void cache_open(void);
|
||
|
-struct nfs_fh_len *
|
||
|
- cache_get_filehandle(nfs_export *exp, int len, char *p);
|
||
|
-int cache_export(nfs_export *exp, char *path);
|
||
|
-
|
||
|
-bool ipaddr_client_matches(nfs_export *exp, struct addrinfo *ai);
|
||
|
-bool namelist_client_matches(nfs_export *exp, char *dom);
|
||
|
-bool client_matches(nfs_export *exp, char *dom, struct addrinfo *ai);
|
||
|
-
|
||
|
#endif /* MOUNTD_H */
|
||
|
diff --git a/utils/mountd/svc_run.c b/utils/mountd/svc_run.c
|
||
|
index 167b975..2aaf375 100644
|
||
|
--- a/utils/mountd/svc_run.c
|
||
|
+++ b/utils/mountd/svc_run.c
|
||
|
@@ -97,28 +97,13 @@ my_svc_run(void)
|
||
|
int selret;
|
||
|
|
||
|
for (;;) {
|
||
|
-
|
||
|
readfds = svc_fdset;
|
||
|
- cache_set_fds(&readfds);
|
||
|
- v4clients_set_fds(&readfds);
|
||
|
-
|
||
|
- selret = select(FD_SETSIZE, &readfds,
|
||
|
- (void *) 0, (void *) 0, (struct timeval *) 0);
|
||
|
-
|
||
|
-
|
||
|
- switch (selret) {
|
||
|
- case -1:
|
||
|
- if (errno == EINTR || errno == ECONNREFUSED
|
||
|
- || errno == ENETUNREACH || errno == EHOSTUNREACH)
|
||
|
- continue;
|
||
|
+ selret = cache_process(&readfds);
|
||
|
+ if (selret < 0) {
|
||
|
xlog(L_ERROR, "my_svc_run() - select: %m");
|
||
|
return;
|
||
|
-
|
||
|
- default:
|
||
|
- selret -= cache_process_req(&readfds);
|
||
|
- selret -= v4clients_process(&readfds);
|
||
|
- if (selret)
|
||
|
- svc_getreqset(&readfds);
|
||
|
}
|
||
|
+ if (selret)
|
||
|
+ svc_getreqset(&readfds);
|
||
|
}
|
||
|
}
|
||
|
diff --git a/utils/statd/start-statd b/utils/statd/start-statd
|
||
|
index 2baf73c..b11a7d9 100755
|
||
|
--- a/utils/statd/start-statd
|
||
|
+++ b/utils/statd/start-statd
|
||
|
@@ -11,8 +11,8 @@ exec 9> /run/rpc.statd.lock
|
||
|
flock -e 9
|
||
|
|
||
|
if [ -s /run/rpc.statd.pid ] &&
|
||
|
- [ 1`cat /run/rpc.statd.pid` -gt 1 ] &&
|
||
|
- kill -0 `cat /run/rpc.statd.pid` > /dev/null 2>&1
|
||
|
+ [ "1$(cat /run/rpc.statd.pid)" -gt 1 ] &&
|
||
|
+ kill -0 "$(cat /run/rpc.statd.pid)" > /dev/null 2>&1
|
||
|
then
|
||
|
# statd already running - must have been slow to respond.
|
||
|
exit 0
|