Compare commits

...

8 Commits

Author SHA1 Message Date
CentOS Sources
c713474ed8 import nfs-utils-2.3.3-59.el8 2023-05-17 02:22:49 +00:00
CentOS Sources
6d474dfbc4 import nfs-utils-2.3.3-57.el8_7.1 2023-02-21 08:47:38 +00:00
CentOS Sources
944240a27c import nfs-utils-2.3.3-57.el8 2022-11-08 09:19:40 +00:00
CentOS Sources
d551f79e6d import nfs-utils-2.3.3-51.el8 2022-05-10 11:03:20 +00:00
CentOS Sources
39bd9e53dd import nfs-utils-2.3.3-46.el8 2021-11-09 10:06:44 +00:00
CentOS Sources
0507dd6161 import nfs-utils-2.3.3-41.el8_4.3 2021-11-02 16:53:35 +00:00
CentOS Sources
b7b45d88ad import nfs-utils-2.3.3-41.el8_4.2 2021-09-09 22:48:33 +00:00
CentOS Sources
1292498303 import nfs-utils-2.3.3-41.el8 2021-09-09 22:48:30 +00:00
30 changed files with 5064 additions and 25 deletions

View File

@ -1,9 +1,9 @@
diff -up nfs-utils-2.3.1/systemd/nfs-server.service.orig nfs-utils-2.3.1/systemd/nfs-server.service
--- nfs-utils-2.3.1/systemd/nfs-server.service.orig 2018-01-19 10:25:38.153513857 -0500
+++ nfs-utils-2.3.1/systemd/nfs-server.service 2018-01-19 10:30:52.977245126 -0500
@@ -26,6 +26,7 @@ Type=oneshot
diff -up nfs-utils-2.3.3/systemd/nfs-server.service.orig nfs-utils-2.3.3/systemd/nfs-server.service
--- nfs-utils-2.3.3/systemd/nfs-server.service.orig 2020-12-11 09:05:23.499222371 -0500
+++ nfs-utils-2.3.3/systemd/nfs-server.service 2020-12-11 09:06:38.970186395 -0500
@@ -23,6 +23,7 @@ Type=oneshot
RemainAfterExit=yes
ExecStartPre=/usr/sbin/exportfs -r
ExecStartPre=-/usr/sbin/exportfs -r
ExecStart=/usr/sbin/rpc.nfsd
+ExecStart=-/bin/sh -c 'if systemctl -q is-active gssproxy; then systemctl reload gssproxy ; fi'
ExecStop=/usr/sbin/rpc.nfsd 0

View File

@ -0,0 +1,25 @@
diff -up nfs-utils-2.3.3/support/export/client.c.orig nfs-utils-2.3.3/support/export/client.c
--- nfs-utils-2.3.3/support/export/client.c.orig 2018-09-06 14:09:08.000000000 -0400
+++ nfs-utils-2.3.3/support/export/client.c 2023-01-12 08:59:44.171609492 -0500
@@ -689,6 +689,9 @@ check_netgroup(const nfs_client *clp, co
/* check whether the IP itself is in the netgroup */
ip = calloc(INET6_ADDRSTRLEN, 1);
+ if (ip == NULL)
+ goto out;
+
if (inet_ntop(ai->ai_family, &(((struct sockaddr_in *)ai->ai_addr)->sin_addr), ip, INET6_ADDRSTRLEN) == ip) {
if (innetgr(netgroup, ip, NULL, NULL)) {
free(hname);
diff -up nfs-utils-2.3.3/tools/nfsrahead/main.c.orig nfs-utils-2.3.3/tools/nfsrahead/main.c
--- nfs-utils-2.3.3/tools/nfsrahead/main.c.orig 2023-01-12 08:58:28.297466979 -0500
+++ nfs-utils-2.3.3/tools/nfsrahead/main.c 2023-01-12 09:00:37.988419866 -0500
@@ -167,7 +167,7 @@ int main(int argc, char **argv)
if ((ret = get_device_info(argv[optind], &device)) == 0)
break;
- if (ret != 0) {
+ if (ret != 0 || device.fstype == NULL) {
xlog(D_GENERAL, "unable to find device %s\n", argv[optind]);
goto out;
}

View File

@ -0,0 +1,50 @@
commit 7d5dcd2358df55353eed94a0e84b77bb3597634e
Author: J. Bruce Fields <bfields@redhat.com>
Date: Fri Mar 27 13:11:28 2020 -0400
exports man page: warn about subdirectory exports
Subdirectory exports have a number of problems which have been poorly
documented.
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
diff --git a/utils/exportfs/exports.man b/utils/exportfs/exports.man
index e3a16f6b..1d171849 100644
--- a/utils/exportfs/exports.man
+++ b/utils/exportfs/exports.man
@@ -494,6 +494,33 @@ export entry for
.B /home/joe
in the example section below, which maps all requests to uid 150 (which
is supposedly that of user joe).
+
+.SS Subdirectory Exports
+
+Normally you should only export only the root of a filesystem. The NFS
+server will also allow you to export a subdirectory of a filesystem,
+however, this has drawbacks:
+
+First, it may be possible for a malicious user to access files on the
+filesystem outside of the exported subdirectory, by guessing filehandles
+for those other files. The only way to prevent this is by using the
+.IR no_subtree_check
+option, which can cause other problems.
+
+Second, export options may not be enforced in the way that you would
+expect. For example, the
+.IR security_label
+option will not work on subdirectory exports, and if nested subdirectory
+exports change the
+.IR security_label
+or
+.IR sec=
+options, NFSv4 clients will normally see only the options on the parent
+export. Also, where security options differ, a malicious client may use
+filehandle-guessing attacks to access the files from one subdirectory
+using the options from another.
+
+
.SS Extra Export Tables
After reading
.I /etc/exports

View File

@ -0,0 +1,43 @@
commit ac266e2edc4f40eef810d52c72657b645e4010db
Author: Ondrej Mosnacek <omosnace@redhat.com>
Date: Tue Apr 6 15:57:37 2021 -0400
exportfs: fix unexporting of '/'
The code that has been added to strip trailing slashes from path in
unexportfs_parsed() forgot to account for the case of the root
directory, which is simply '/'. In that case it accesses path[-1] and
reduces the path to an empty string, which then fails to match any
export.
Fix it by stopping the stripping when the path is just a single
character - it doesn't matter if it's a '/' or not, we want to keep it
either way in that case.
Reproducer:
exportfs localhost:/
exportfs -u localhost:/
Without this patch, the unexport step fails with "exportfs: Could not
find 'localhost:/' to unexport."
Fixes: a9a7728d8743 ("exportfs: Deal with path's trailing "/" in unexportfs_parsed()")
Link: https://bugzilla.redhat.com/show_bug.cgi?id=1941171
Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c
index 262dd19a..25d757d8 100644
--- a/utils/exportfs/exportfs.c
+++ b/utils/exportfs/exportfs.c
@@ -383,7 +383,7 @@ unexportfs_parsed(char *hname, char *path, int verbose)
* so need to deal with it.
*/
size_t nlen = strlen(path);
- while (path[nlen - 1] == '/')
+ while ((nlen > 1) && (path[nlen - 1] == '/'))
nlen--;
for (exp = exportlist[htype].p_head; exp; exp = exp->m_next) {

View File

@ -0,0 +1,30 @@
commit ba90d61be3abca5a699765ce08759ca6b986781d
Author: Steve Dickson <steved@redhat.com>
Date: Thu Dec 10 14:05:23 2020 -0500
exports.man: Remove some outdated verbiage
Years ago, commit 6a7d90cea765 removed the warning
this verbiage was talking about, but was never
removed from the man page.
Signed-off-by: Steve Dickson <steved@redhat.com>
diff --git a/utils/exportfs/exports.man b/utils/exportfs/exports.man
index 1d171849..54b3f877 100644
--- a/utils/exportfs/exports.man
+++ b/utils/exportfs/exports.man
@@ -169,13 +169,6 @@ default. In all releases after 1.0.0,
is the default, and
.I async
must be explicitly requested if needed.
-To help make system administrators aware of this change,
-.B exportfs
-will issue a warning if neither
-.I sync
-nor
-.I async
-is specified.
.TP
.IR no_wdelay
This option has no effect if

View File

@ -0,0 +1,290 @@
diff -up nfs-utils-2.3.3/utils/gssd/err_util.c.orig nfs-utils-2.3.3/utils/gssd/err_util.c
--- nfs-utils-2.3.3/utils/gssd/err_util.c.orig 2018-09-06 14:09:08.000000000 -0400
+++ nfs-utils-2.3.3/utils/gssd/err_util.c 2021-07-19 12:29:21.366829573 -0400
@@ -70,3 +70,17 @@ int get_verbosity(void)
{
return verbosity;
}
+
+char *
+sec2time(int value)
+{
+ static char buf[BUFSIZ];
+ int hr, min, sec;
+
+ hr = (value / 3600);
+ min = (value - (3600*hr))/60;
+ sec = (value - (3600*hr) - (min*60));
+ sprintf(buf, "%dh:%dm:%ds", hr, min, sec);
+ return(buf);
+}
+
diff -up nfs-utils-2.3.3/utils/gssd/err_util.h.orig nfs-utils-2.3.3/utils/gssd/err_util.h
--- nfs-utils-2.3.3/utils/gssd/err_util.h.orig 2018-09-06 14:09:08.000000000 -0400
+++ nfs-utils-2.3.3/utils/gssd/err_util.h 2021-07-19 12:29:21.367829599 -0400
@@ -34,5 +34,6 @@
void initerr(char *progname, int verbosity, int fg);
void printerr(int priority, char *format, ...);
int get_verbosity(void);
+char * sec2time(int);
#endif /* _ERR_UTIL_H_ */
diff -up nfs-utils-2.3.3/utils/gssd/gssd.c.orig nfs-utils-2.3.3/utils/gssd/gssd.c
--- nfs-utils-2.3.3/utils/gssd/gssd.c.orig 2021-07-19 12:24:13.963644016 -0400
+++ nfs-utils-2.3.3/utils/gssd/gssd.c 2021-07-19 12:29:21.368829626 -0400
@@ -396,7 +396,7 @@ gssd_free_client(struct clnt_info *clp)
if (refcnt > 0)
return;
- printerr(3, "freeing client %s\n", clp->relpath);
+ printerr(4, "freeing client %s\n", clp->relpath);
if (clp->krb5_fd >= 0)
close(clp->krb5_fd);
@@ -417,7 +417,7 @@ gssd_free_client(struct clnt_info *clp)
static void
gssd_destroy_client(struct clnt_info *clp)
{
- printerr(3, "destroying client %s\n", clp->relpath);
+ printerr(4, "destroying client %s\n", clp->relpath);
if (clp->krb5_ev) {
event_del(clp->krb5_ev);
@@ -494,7 +494,7 @@ scan_active_thread_list(void)
* upcall_thread_info from the list and free it.
*/
if (tret == PTHREAD_CANCELED)
- printerr(3, "watchdog: thread id 0x%lx cancelled successfully\n",
+ printerr(2, "watchdog: thread id 0x%lx cancelled successfully\n",
info->tid);
saveprev = info->list.tqe_prev;
TAILQ_REMOVE(&active_thread_list, info, list);
@@ -783,7 +783,7 @@ gssd_scan(void)
{
struct dirent *d;
- printerr(3, "doing a full rescan\n");
+ printerr(4, "doing a full rescan\n");
rewinddir(pipefs_dir);
while ((d = readdir(pipefs_dir))) {
diff -up nfs-utils-2.3.3/utils/gssd/gssd_proc.c.orig nfs-utils-2.3.3/utils/gssd/gssd_proc.c
--- nfs-utils-2.3.3/utils/gssd/gssd_proc.c.orig 2021-07-19 12:24:13.964644043 -0400
+++ nfs-utils-2.3.3/utils/gssd/gssd_proc.c 2021-07-19 12:29:21.368829626 -0400
@@ -166,8 +166,9 @@ do_downcall(int k5_fd, uid_t uid, struct
unsigned int buf_size = 0;
pthread_t tid = pthread_self();
- printerr(2, "do_downcall(0x%x): lifetime_rec=%u acceptor=%.*s\n",
- tid, lifetime_rec, acceptor->length, acceptor->value);
+ if (get_verbosity() > 1)
+ printerr(2, "do_downcall(0x%lx): lifetime_rec=%s acceptor=%.*s\n",
+ tid, sec2time(lifetime_rec), acceptor->length, acceptor->value);
buf_size = sizeof(uid) + sizeof(timeout) + sizeof(pd->pd_seq_win) +
sizeof(pd->pd_ctx_hndl.length) + pd->pd_ctx_hndl.length +
sizeof(context_token->length) + context_token->length +
@@ -193,7 +194,7 @@ do_downcall(int k5_fd, uid_t uid, struct
return;
out_err:
free(buf);
- printerr(1, "do_downcall(0x%x): Failed to write downcall!\n", tid);
+ printerr(1, "do_downcall(0x%lx): Failed to write downcall!\n", tid);
return;
}
@@ -204,8 +205,9 @@ do_error_downcall(int k5_fd, uid_t uid,
char *p = buf, *end = buf + 1024;
unsigned int timeout = 0;
int zero = 0;
+ pthread_t tid = pthread_self();
- printerr(2, "doing error downcall\n");
+ printerr(2, "do_error_downcall(0x%lx): uid %d err %d\n", tid, uid, err);
if (WRITE_BYTES(&p, end, uid)) goto out_err;
if (WRITE_BYTES(&p, end, timeout)) goto out_err;
@@ -328,6 +330,7 @@ create_auth_rpc_client(struct clnt_info
struct timeval timeout;
struct sockaddr *addr = (struct sockaddr *) &clp->addr;
socklen_t salen;
+ pthread_t tid = pthread_self();
sec.qop = GSS_C_QOP_DEFAULT;
sec.svc = RPCSEC_GSS_SVC_NONE;
@@ -361,8 +364,8 @@ create_auth_rpc_client(struct clnt_info
/* create an rpc connection to the nfs server */
- printerr(2, "creating %s client for server %s\n", clp->protocol,
- clp->servername);
+ printerr(3, "create_auth_rpc_client(0x%lx): creating %s client for server %s\n",
+ tid, clp->protocol, clp->servername);
protocol = IPPROTO_TCP;
if ((strcmp(clp->protocol, "udp")) == 0)
@@ -405,7 +408,8 @@ create_auth_rpc_client(struct clnt_info
if (!tgtname)
tgtname = clp->servicename;
- printerr(2, "creating context with server %s\n", tgtname);
+ printerr(3, "create_auth_rpc_client(0x%lx): creating context with server %s\n",
+ tid, tgtname);
auth = authgss_create_default(rpc_clnt, tgtname, &sec);
if (!auth) {
/* Our caller should print appropriate message */
@@ -507,9 +511,10 @@ krb5_not_machine_creds(struct clnt_info
gss_cred_id_t gss_cred;
char **dname;
int err, resp = -1;
+ pthread_t tid = pthread_self();
- printerr(2, "krb5_not_machine_creds: uid %d tgtname %s\n",
- uid, tgtname);
+ printerr(2, "krb5_not_machine_creds(0x%lx): uid %d tgtname %s\n",
+ tid, uid, tgtname);
*chg_err = change_identity(uid);
if (*chg_err) {
@@ -555,9 +560,10 @@ krb5_use_machine_creds(struct clnt_info
char **ccname;
int nocache = 0;
int success = 0;
+ pthread_t tid = pthread_self();
- printerr(2, "krb5_use_machine_creds: uid %d tgtname %s\n",
- uid, tgtname);
+ printerr(2, "krb5_use_machine_creds(0x%lx): uid %d tgtname %s\n",
+ tid, uid, tgtname);
do {
gssd_refresh_krb5_machine_credential(clp->servername,
@@ -874,6 +880,7 @@ start_upcall_thread(void (*func)(struct
pthread_t th;
struct upcall_thread_info *tinfo;
int ret;
+ pthread_t tid = pthread_self();
tinfo = alloc_upcall_thread_info();
if (!tinfo)
@@ -896,6 +903,9 @@ start_upcall_thread(void (*func)(struct
free(tinfo);
return ret;
}
+ printerr(2, "start_upcall_thread(0x%lx): created thread id 0x%lx\n",
+ tid, th);
+
tinfo->tid = th;
pthread_mutex_lock(&active_thread_list_lock);
clock_gettime(CLOCK_MONOTONIC, &tinfo->timeout);
@@ -958,7 +968,7 @@ handle_gssd_upcall(struct clnt_info *clp
}
lbuf[lbuflen-1] = 0;
- printerr(2, "\n%s(0x%x): '%s' (%s)\n", __func__, tid,
+ printerr(2, "\n%s(0x%lx): '%s' (%s)\n", __func__, tid,
lbuf, clp->relpath);
for (p = strtok(lbuf, " "); p; p = strtok(NULL, " ")) {
diff -up nfs-utils-2.3.3/utils/gssd/krb5_util.c.orig nfs-utils-2.3.3/utils/gssd/krb5_util.c
--- nfs-utils-2.3.3/utils/gssd/krb5_util.c.orig 2021-07-19 12:24:13.951643697 -0400
+++ nfs-utils-2.3.3/utils/gssd/krb5_util.c 2021-07-19 12:36:27.746223992 -0400
@@ -375,6 +375,7 @@ gssd_get_single_krb5_cred(krb5_context c
char *cache_type;
char *pname = NULL;
char *k5err = NULL;
+ pthread_t tid = pthread_self();
memset(&my_creds, 0, sizeof(my_creds));
@@ -385,8 +386,8 @@ gssd_get_single_krb5_cred(krb5_context c
now += 300;
pthread_mutex_lock(&ple_lock);
if (ple->ccname && ple->endtime > now && !nocache) {
- printerr(3, "INFO: Credentials in CC '%s' are good until %d\n",
- ple->ccname, ple->endtime);
+ printerr(3, "%s(0x%lx): Credentials in CC '%s' are good until %s",
+ __func__, tid, ple->ccname, ctime((time_t *)&ple->endtime));
code = 0;
pthread_mutex_unlock(&ple_lock);
goto out;
@@ -486,7 +487,8 @@ gssd_get_single_krb5_cred(krb5_context c
}
code = 0;
- printerr(2, "%s: principal '%s' ccache:'%s'\n", __func__, pname, cc_name);
+ printerr(2, "%s(0x%lx): principal '%s' ccache:'%s'\n",
+ __func__, tid, pname, cc_name);
out:
#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS
if (init_opts)
@@ -615,6 +617,7 @@ get_full_hostname(const char *inhost, ch
struct addrinfo hints;
int retval;
char *c;
+ pthread_t tid = pthread_self();
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
@@ -624,8 +627,8 @@ get_full_hostname(const char *inhost, ch
/* Get full target hostname */
retval = getaddrinfo(inhost, NULL, &hints, &addrs);
if (retval) {
- printerr(1, "%s while getting full hostname for '%s'\n",
- gai_strerror(retval), inhost);
+ printerr(1, "%s(0x%lx): getaddrinfo(%s) failed: %s\n",
+ __func__, tid, inhost, gai_strerror(retval));
goto out;
}
strncpy(outhost, addrs->ai_canonname, outhostlen);
@@ -633,7 +636,10 @@ get_full_hostname(const char *inhost, ch
for (c = outhost; *c != '\0'; c++)
*c = tolower(*c);
- printerr(3, "Full hostname for '%s' is '%s'\n", inhost, outhost);
+ if (get_verbosity() && strcmp(inhost, outhost))
+ printerr(1, "%s(0x%0lx): inhost '%s' different than outhost'%s'\n",
+ inhost, outhost);
+
retval = 0;
out:
return retval;
@@ -819,6 +825,7 @@ find_keytab_entry(krb5_context context,
krb5_principal princ;
const char *notsetstr = "not set";
char *adhostoverride = NULL;
+ pthread_t tid = pthread_self();
/* Get full target hostname */
@@ -972,7 +979,7 @@ find_keytab_entry(krb5_context context,
tried_upper = 1;
}
} else {
- printerr(2, "Success getting keytab entry for '%s'\n",spn);
+ printerr(2, "find_keytab_entry(0x%lx): Success getting keytab entry for '%s'\n",tid, spn);
retval = 0;
goto out;
}
@@ -1113,9 +1120,6 @@ gssd_refresh_krb5_machine_credential_int
char *k5err = NULL;
const char *svcnames[] = { "$", "root", "nfs", "host", NULL };
- printerr(2, "%s: hostname=%s ple=%p service=%s srchost=%s\n",
- __func__, hostname, ple, service, srchost);
-
/*
* If a specific service name was specified, use it.
* Otherwise, use the default list.
@@ -1124,9 +1128,10 @@ gssd_refresh_krb5_machine_credential_int
svcnames[0] = service;
svcnames[1] = NULL;
}
- if (hostname == NULL && ple == NULL)
+ if (hostname == NULL && ple == NULL) {
+ printerr(0, "ERROR: %s: Invalid args\n", __func__);
return EINVAL;
-
+ }
code = krb5_init_context(&context);
if (code) {
k5err = gssd_k5_err_msg(NULL, code);

View File

@ -0,0 +1,402 @@
diff -up nfs-utils-2.3.3/utils/gssd/gssd.c.orig nfs-utils-2.3.3/utils/gssd/gssd.c
--- nfs-utils-2.3.3/utils/gssd/gssd.c.orig 2021-07-19 09:39:04.273895536 -0400
+++ nfs-utils-2.3.3/utils/gssd/gssd.c 2021-07-19 09:40:13.942751214 -0400
@@ -364,7 +364,7 @@ out:
/* Actually frees clp and fields that might be used from other
* threads if was last reference.
*/
-static void
+void
gssd_free_client(struct clnt_info *clp)
{
int refcnt;
@@ -416,55 +416,6 @@ gssd_destroy_client(struct clnt_info *cl
static void gssd_scan(void);
-static int
-start_upcall_thread(void (*func)(struct clnt_upcall_info *), void *info)
-{
- pthread_attr_t attr;
- pthread_t th;
- int ret;
-
- ret = pthread_attr_init(&attr);
- if (ret != 0) {
- printerr(0, "ERROR: failed to init pthread attr: ret %d: %s\n",
- ret, strerror(errno));
- return ret;
- }
- ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
- if (ret != 0) {
- printerr(0, "ERROR: failed to create pthread attr: ret %d: "
- "%s\n", ret, strerror(errno));
- return ret;
- }
-
- ret = pthread_create(&th, &attr, (void *)func, (void *)info);
- if (ret != 0)
- printerr(0, "ERROR: pthread_create failed: ret %d: %s\n",
- ret, strerror(errno));
- return ret;
-}
-
-static struct clnt_upcall_info *alloc_upcall_info(struct clnt_info *clp)
-{
- struct clnt_upcall_info *info;
-
- info = malloc(sizeof(struct clnt_upcall_info));
- if (info == NULL)
- return NULL;
-
- pthread_mutex_lock(&clp_lock);
- clp->refcount++;
- pthread_mutex_unlock(&clp_lock);
- info->clp = clp;
-
- return info;
-}
-
-void free_upcall_info(struct clnt_upcall_info *info)
-{
- gssd_free_client(info->clp);
- free(info);
-}
-
/* For each upcall read the upcall info into the buffer, then create a
* thread in a detached state so that resources are released back into
* the system without the need for a join.
@@ -473,44 +424,16 @@ static void
gssd_clnt_gssd_cb(int UNUSED(fd), short UNUSED(which), void *data)
{
struct clnt_info *clp = data;
- struct clnt_upcall_info *info;
-
- info = alloc_upcall_info(clp);
- if (info == NULL)
- return;
-
- info->lbuflen = read(clp->gssd_fd, info->lbuf, sizeof(info->lbuf));
- if (info->lbuflen <= 0 || info->lbuf[info->lbuflen-1] != '\n') {
- printerr(0, "WARNING: %s: failed reading request\n", __func__);
- free_upcall_info(info);
- return;
- }
- info->lbuf[info->lbuflen-1] = 0;
- if (start_upcall_thread(handle_gssd_upcall, info))
- free_upcall_info(info);
+ handle_gssd_upcall(clp);
}
static void
gssd_clnt_krb5_cb(int UNUSED(fd), short UNUSED(which), void *data)
{
struct clnt_info *clp = data;
- struct clnt_upcall_info *info;
-
- info = alloc_upcall_info(clp);
- if (info == NULL)
- return;
-
- if (read(clp->krb5_fd, &info->uid,
- sizeof(info->uid)) < (ssize_t)sizeof(info->uid)) {
- printerr(0, "WARNING: %s: failed reading uid from krb5 "
- "upcall pipe: %s\n", __func__, strerror(errno));
- free_upcall_info(info);
- return;
- }
- if (start_upcall_thread(handle_krb5_upcall, info))
- free_upcall_info(info);
+ handle_krb5_upcall(clp);
}
static struct clnt_info *
diff -up nfs-utils-2.3.3/utils/gssd/gssd.h.orig nfs-utils-2.3.3/utils/gssd/gssd.h
--- nfs-utils-2.3.3/utils/gssd/gssd.h.orig 2021-07-19 09:39:04.269895430 -0400
+++ nfs-utils-2.3.3/utils/gssd/gssd.h 2021-07-19 09:40:13.943751240 -0400
@@ -84,14 +84,17 @@ struct clnt_info {
struct clnt_upcall_info {
struct clnt_info *clp;
- char lbuf[RPC_CHAN_BUF_SIZE];
- int lbuflen;
uid_t uid;
+ int fd;
+ char *srchost;
+ char *target;
+ char *service;
};
-void handle_krb5_upcall(struct clnt_upcall_info *clp);
-void handle_gssd_upcall(struct clnt_upcall_info *clp);
+void handle_krb5_upcall(struct clnt_info *clp);
+void handle_gssd_upcall(struct clnt_info *clp);
void free_upcall_info(struct clnt_upcall_info *info);
+void gssd_free_client(struct clnt_info *clp);
#endif /* _RPC_GSSD_H_ */
diff -up nfs-utils-2.3.3/utils/gssd/gssd_proc.c.orig nfs-utils-2.3.3/utils/gssd/gssd_proc.c
--- nfs-utils-2.3.3/utils/gssd/gssd_proc.c.orig 2021-07-19 09:39:04.269895430 -0400
+++ nfs-utils-2.3.3/utils/gssd/gssd_proc.c 2021-07-19 09:40:13.944751267 -0400
@@ -80,6 +80,8 @@
#include "nfslib.h"
#include "gss_names.h"
+extern pthread_mutex_t clp_lock;
+
/* Encryption types supported by the kernel rpcsec_gss code */
int num_krb5_enctypes = 0;
krb5_enctype *krb5_enctypes = NULL;
@@ -719,22 +721,133 @@ out_return_error:
goto out;
}
-void
-handle_krb5_upcall(struct clnt_upcall_info *info)
-{
- struct clnt_info *clp = info->clp;
+static struct clnt_upcall_info *
+alloc_upcall_info(struct clnt_info *clp, uid_t uid, int fd, char *srchost,
+ char *target, char *service)
+{
+ struct clnt_upcall_info *info;
+
+ info = malloc(sizeof(struct clnt_upcall_info));
+ if (info == NULL)
+ return NULL;
+
+ memset(info, 0, sizeof(*info));
+ pthread_mutex_lock(&clp_lock);
+ clp->refcount++;
+ pthread_mutex_unlock(&clp_lock);
+ info->clp = clp;
+ info->uid = uid;
+ info->fd = fd;
+ if (srchost) {
+ info->srchost = strdup(srchost);
+ if (info->srchost == NULL)
+ goto out_info;
+ }
+ if (target) {
+ info->target = strdup(target);
+ if (info->target == NULL)
+ goto out_srchost;
+ }
+ if (service) {
+ info->service = strdup(service);
+ if (info->service == NULL)
+ goto out_target;
+ }
+
+out:
+ return info;
- printerr(2, "\n%s: uid %d (%s)\n", __func__, info->uid, clp->relpath);
+out_target:
+ if (info->target)
+ free(info->target);
+out_srchost:
+ if (info->srchost)
+ free(info->srchost);
+out_info:
+ free(info);
+ info = NULL;
+ goto out;
+}
+
+void free_upcall_info(struct clnt_upcall_info *info)
+{
+ gssd_free_client(info->clp);
+ if (info->service)
+ free(info->service);
+ if (info->target)
+ free(info->target);
+ if (info->srchost)
+ free(info->srchost);
+ free(info);
+}
- process_krb5_upcall(clp, info->uid, clp->krb5_fd, NULL, NULL, NULL);
+static void
+gssd_work_thread_fn(struct clnt_upcall_info *info)
+{
+ process_krb5_upcall(info->clp, info->uid, info->fd, info->srchost, info->target, info->service);
free_upcall_info(info);
}
+static int
+start_upcall_thread(void (*func)(struct clnt_upcall_info *), void *info)
+{
+ pthread_attr_t attr;
+ pthread_t th;
+ int ret;
+
+ ret = pthread_attr_init(&attr);
+ if (ret != 0) {
+ printerr(0, "ERROR: failed to init pthread attr: ret %d: %s\n",
+ ret, strerror(errno));
+ return ret;
+ }
+ ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (ret != 0) {
+ printerr(0, "ERROR: failed to create pthread attr: ret %d: "
+ "%s\n", ret, strerror(errno));
+ return ret;
+ }
+
+ ret = pthread_create(&th, &attr, (void *)func, (void *)info);
+ if (ret != 0)
+ printerr(0, "ERROR: pthread_create failed: ret %d: %s\n",
+ ret, strerror(errno));
+ return ret;
+}
+
void
-handle_gssd_upcall(struct clnt_upcall_info *info)
+handle_krb5_upcall(struct clnt_info *clp)
{
- struct clnt_info *clp = info->clp;
uid_t uid;
+ struct clnt_upcall_info *info;
+ int err;
+
+ if (read(clp->krb5_fd, &uid, sizeof(uid)) < (ssize_t)sizeof(uid)) {
+ printerr(0, "WARNING: failed reading uid from krb5 "
+ "upcall pipe: %s\n", strerror(errno));
+ return;
+ }
+ printerr(2, "\n%s: uid %d (%s)\n", __func__, uid, clp->relpath);
+
+ info = alloc_upcall_info(clp, uid, clp->krb5_fd, NULL, NULL, NULL);
+ if (info == NULL) {
+ printerr(0, "%s: failed to allocate clnt_upcall_info\n", __func__);
+ do_error_downcall(clp->krb5_fd, uid, -EACCES);
+ return;
+ }
+ err = start_upcall_thread(gssd_work_thread_fn, info);
+ if (err != 0) {
+ do_error_downcall(clp->krb5_fd, uid, -EACCES);
+ free_upcall_info(info);
+ }
+}
+
+void
+handle_gssd_upcall(struct clnt_info *clp)
+{
+ uid_t uid;
+ char lbuf[RPC_CHAN_BUF_SIZE];
+ int lbuflen = 0;
char *p;
char *mech = NULL;
char *uidstr = NULL;
@@ -742,20 +855,22 @@ handle_gssd_upcall(struct clnt_upcall_in
char *service = NULL;
char *srchost = NULL;
char *enctypes = NULL;
- char *upcall_str;
- char *pbuf = info->lbuf;
pthread_t tid = pthread_self();
+ struct clnt_upcall_info *info;
+ int err;
- printerr(2, "\n%s(0x%x): '%s' (%s)\n", __func__, tid,
- info->lbuf, clp->relpath);
-
- upcall_str = strdup(info->lbuf);
- if (upcall_str == NULL) {
- printerr(0, "ERROR: malloc failure\n");
- goto out_nomem;
+ lbuflen = read(clp->gssd_fd, lbuf, sizeof(lbuf));
+ if (lbuflen <= 0 || lbuf[lbuflen-1] != '\n') {
+ printerr(0, "WARNING: handle_gssd_upcall: "
+ "failed reading request\n");
+ return;
}
+ lbuf[lbuflen-1] = 0;
+
+ printerr(2, "\n%s(0x%x): '%s' (%s)\n", __func__, tid,
+ lbuf, clp->relpath);
- while ((p = strsep(&pbuf, " "))) {
+ for (p = strtok(lbuf, " "); p; p = strtok(NULL, " ")) {
if (!strncmp(p, "mech=", strlen("mech=")))
mech = p + strlen("mech=");
else if (!strncmp(p, "uid=", strlen("uid=")))
@@ -773,8 +888,8 @@ handle_gssd_upcall(struct clnt_upcall_in
if (!mech || strlen(mech) < 1) {
printerr(0, "WARNING: handle_gssd_upcall: "
"failed to find gss mechanism name "
- "in upcall string '%s'\n", upcall_str);
- goto out;
+ "in upcall string '%s'\n", lbuf);
+ return;
}
if (uidstr) {
@@ -786,21 +901,21 @@ handle_gssd_upcall(struct clnt_upcall_in
if (!uidstr) {
printerr(0, "WARNING: handle_gssd_upcall: "
"failed to find uid "
- "in upcall string '%s'\n", upcall_str);
- goto out;
+ "in upcall string '%s'\n", lbuf);
+ return;
}
if (enctypes && parse_enctypes(enctypes) != 0) {
printerr(0, "WARNING: handle_gssd_upcall: "
"parsing encryption types failed: errno %d\n", errno);
- goto out;
+ return;
}
if (target && strlen(target) < 1) {
printerr(0, "WARNING: handle_gssd_upcall: "
"failed to parse target name "
- "in upcall string '%s'\n", upcall_str);
- goto out;
+ "in upcall string '%s'\n", lbuf);
+ return;
}
/*
@@ -814,21 +929,26 @@ handle_gssd_upcall(struct clnt_upcall_in
if (service && strlen(service) < 1) {
printerr(0, "WARNING: handle_gssd_upcall: "
"failed to parse service type "
- "in upcall string '%s'\n", upcall_str);
- goto out;
+ "in upcall string '%s'\n", lbuf);
+ return;
}
- if (strcmp(mech, "krb5") == 0 && clp->servername)
- process_krb5_upcall(clp, uid, clp->gssd_fd, srchost, target, service);
- else {
+ if (strcmp(mech, "krb5") == 0 && clp->servername) {
+ info = alloc_upcall_info(clp, uid, clp->gssd_fd, srchost, target, service);
+ if (info == NULL) {
+ printerr(0, "%s: failed to allocate clnt_upcall_info\n", __func__);
+ do_error_downcall(clp->gssd_fd, uid, -EACCES);
+ return;
+ }
+ err = start_upcall_thread(gssd_work_thread_fn, info);
+ if (err != 0) {
+ do_error_downcall(clp->gssd_fd, uid, -EACCES);
+ free_upcall_info(info);
+ }
+ } else {
if (clp->servername)
printerr(0, "WARNING: handle_gssd_upcall: "
"received unknown gss mech '%s'\n", mech);
do_error_downcall(clp->gssd_fd, uid, -EACCES);
}
-out:
- free(upcall_str);
-out_nomem:
- free_upcall_info(info);
- return;
}

View File

@ -0,0 +1,141 @@
diff -up nfs-utils-2.3.3/nfs.conf.orig nfs-utils-2.3.3/nfs.conf
--- nfs-utils-2.3.3/nfs.conf.orig 2021-04-17 10:49:23.660184527 -0400
+++ nfs-utils-2.3.3/nfs.conf 2021-04-17 11:14:41.482108562 -0400
@@ -21,6 +21,7 @@ use-gss-proxy=1
# keytab-file=/etc/krb5.keytab
# cred-cache-directory=
# preferred-realm=
+# set-home=1
#
[lockd]
# port=0
diff -up nfs-utils-2.3.3/systemd/nfs.conf.man.orig nfs-utils-2.3.3/systemd/nfs.conf.man
--- nfs-utils-2.3.3/systemd/nfs.conf.man.orig 2021-04-17 10:49:23.696185472 -0400
+++ nfs-utils-2.3.3/systemd/nfs.conf.man 2021-04-17 11:14:41.483108588 -0400
@@ -222,7 +222,8 @@ Recognized values:
.BR rpc-timeout ,
.BR keytab-file ,
.BR cred-cache-directory ,
-.BR preferred-realm .
+.BR preferred-realm ,
+.BR set-home .
See
.BR rpc.gssd (8)
diff -up nfs-utils-2.3.3/utils/gssd/gssd.c.orig nfs-utils-2.3.3/utils/gssd/gssd.c
--- nfs-utils-2.3.3/utils/gssd/gssd.c.orig 2021-04-17 10:49:23.684185157 -0400
+++ nfs-utils-2.3.3/utils/gssd/gssd.c 2021-04-17 11:14:41.483108588 -0400
@@ -87,6 +87,8 @@ unsigned int context_timeout = 0;
unsigned int rpc_timeout = 5;
char *preferred_realm = NULL;
char *ccachedir = NULL;
+/* set $HOME to "/" by default */
+static bool set_home = true;
/* Avoid DNS reverse lookups on server names */
static bool avoid_dns = true;
static bool use_gssproxy = false;
@@ -885,7 +887,7 @@ sig_die(int signal)
static void
usage(char *progname)
{
- fprintf(stderr, "usage: %s [-f] [-l] [-M] [-n] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir] [-t timeout] [-R preferred realm] [-D]\n",
+ fprintf(stderr, "usage: %s [-f] [-l] [-M] [-n] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir] [-t timeout] [-R preferred realm] [-D] [-H]\n",
progname);
exit(1);
}
@@ -926,6 +928,7 @@ read_gss_conf(void)
preferred_realm = s;
use_gssproxy = conf_get_bool("gssd", "use-gss-proxy", use_gssproxy);
+ set_home = conf_get_bool("gssd", "set-home", set_home);
}
int
@@ -946,7 +949,7 @@ main(int argc, char *argv[])
verbosity = conf_get_num("gssd", "verbosity", verbosity);
rpc_verbosity = conf_get_num("gssd", "rpc-verbosity", rpc_verbosity);
- while ((opt = getopt(argc, argv, "DfvrlmnMp:k:d:t:T:R:")) != -1) {
+ while ((opt = getopt(argc, argv, "HDfvrlmnMp:k:d:t:T:R:")) != -1) {
switch (opt) {
case 'f':
fg = 1;
@@ -994,6 +997,9 @@ main(int argc, char *argv[])
case 'D':
avoid_dns = false;
break;
+ case 'H':
+ set_home = false;
+ break;
default:
usage(argv[0]);
break;
@@ -1003,13 +1009,19 @@ main(int argc, char *argv[])
/*
* Some krb5 routines try to scrape info out of files in the user's
* home directory. This can easily deadlock when that homedir is on a
- * kerberized NFS mount. By setting $HOME unconditionally to "/", we
- * prevent this behavior in routines that use $HOME in preference to
- * the results of getpw*.
+ * kerberized NFS mount. By setting $HOME to "/" by default, we prevent
+ * this behavior in routines that use $HOME in preference to the results
+ * of getpw*.
+ *
+ * Some users do not use Kerberized home dirs and need $HOME to remain
+ * unchanged. Those users can leave $HOME unchanged by setting set_home
+ * to false.
*/
- if (setenv("HOME", "/", 1)) {
- printerr(0, "gssd: Unable to set $HOME: %s\n", strerror(errno));
- exit(1);
+ if (set_home) {
+ if (setenv("HOME", "/", 1)) {
+ printerr(0, "gssd: Unable to set $HOME: %s\n", strerror(errno));
+ exit(1);
+ }
}
if (use_gssproxy) {
diff -up nfs-utils-2.3.3/utils/gssd/gssd.man.orig nfs-utils-2.3.3/utils/gssd/gssd.man
--- nfs-utils-2.3.3/utils/gssd/gssd.man.orig 2021-04-17 10:49:23.650184264 -0400
+++ nfs-utils-2.3.3/utils/gssd/gssd.man 2021-04-17 11:14:41.484108615 -0400
@@ -8,7 +8,7 @@
rpc.gssd \- RPCSEC_GSS daemon
.SH SYNOPSIS
.B rpc.gssd
-.RB [ \-DfMnlvr ]
+.RB [ \-DfMnlvrH ]
.RB [ \-k
.IR keytab ]
.RB [ \-p
@@ -297,6 +297,16 @@ The default timeout is set to 5 seconds.
If you get messages like "WARNING: can't create tcp rpc_clnt to server
%servername% for user with uid %uid%: RPC: Remote system error -
Connection timed out", you should consider an increase of this timeout.
+.TP
+.B -H
+Avoids setting $HOME to "/". This allows rpc.gssd to read per user k5identity
+files versus trying to read /.k5identity for each user.
+
+If
+.B \-H
+is not set, rpc.gssd will use the first match found in
+/var/kerberos/krb5/user/$EUID/client.keytab and will not use a principal based on
+host and/or service parameters listed in $HOME/.k5identity.
.SH CONFIGURATION FILE
Many of the options that can be set on the command line can also be
controlled through values set in the
@@ -354,6 +364,13 @@ Equivalent to
.B preferred-realm
Equivalent to
.BR -R .
+.TP
+.B set-home
+Setting to
+.B false
+is equivalent to providing the
+.B -H
+flag.
.P
In addtion, the following value is recognized from the
.B [general]

View File

@ -0,0 +1,17 @@
diff -up nfs-utils-2.3.3/utils/gssd/gssd.man.orig nfs-utils-2.3.3/utils/gssd/gssd.man
--- nfs-utils-2.3.3/utils/gssd/gssd.man.orig 2021-04-17 11:21:18.326543446 -0400
+++ nfs-utils-2.3.3/utils/gssd/gssd.man 2021-04-17 12:35:59.867574517 -0400
@@ -347,11 +347,11 @@ Equivalent to
.TP
.B context-timeout
Equivalent to
-.BR -T .
+.BR -t .
.TP
.B rpc-timeout
Equivalent to
-.BR -t .
+.BR -T .
.TP
.B keytab-file
Equivalent to

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,43 @@
diff -up nfs-utils-2.3.3/utils/gssd/krb5_util.c.orig nfs-utils-2.3.3/utils/gssd/krb5_util.c
--- nfs-utils-2.3.3/utils/gssd/krb5_util.c.orig 2021-07-22 15:27:27.728680553 -0400
+++ nfs-utils-2.3.3/utils/gssd/krb5_util.c 2021-07-22 15:30:08.916979585 -0400
@@ -165,18 +165,28 @@ static int gssd_get_single_krb5_cred(krb
static int query_krb5_ccache(const char* cred_cache, char **ret_princname,
char **ret_realm);
-static void release_ple(krb5_context context, struct gssd_k5_kt_princ *ple)
+static void release_ple_locked(krb5_context context,
+ struct gssd_k5_kt_princ *ple)
{
if (--ple->refcount)
return;
- printerr(3, "freeing cached principal (ccname=%s, realm=%s)\n", ple->ccname, ple->realm);
+ printerr(3, "freeing cached principal (ccname=%s, realm=%s)\n",
+ ple->ccname, ple->realm);
krb5_free_principal(context, ple->princ);
free(ple->ccname);
free(ple->realm);
free(ple);
}
+static void release_ple(krb5_context context, struct gssd_k5_kt_princ *ple)
+{
+ pthread_mutex_lock(&ple_lock);
+ release_ple_locked(context, ple);
+ pthread_mutex_unlock(&ple_lock);
+}
+
+
/*
* Called from the scandir function to weed out potential krb5
* credentials cache files
@@ -1396,7 +1406,7 @@ gssd_destroy_krb5_principals(int destroy
}
}
- release_ple(context, ple);
+ release_ple_locked(context, ple);
}
pthread_mutex_unlock(&ple_lock);
krb5_free_context(context);

View File

@ -0,0 +1,14 @@
diff -up nfs-utils-2.3.3/utils/gssd/krb5_util.c.orig nfs-utils-2.3.3/utils/gssd/krb5_util.c
--- nfs-utils-2.3.3/utils/gssd/krb5_util.c.orig 2021-11-04 10:13:07.788142847 -0400
+++ nfs-utils-2.3.3/utils/gssd/krb5_util.c 2021-11-04 10:14:10.829841090 -0400
@@ -647,8 +647,8 @@ get_full_hostname(const char *inhost, ch
*c = tolower(*c);
if (get_verbosity() && strcmp(inhost, outhost))
- printerr(1, "%s(0x%0lx): inhost '%s' different than outhost'%s'\n",
- inhost, outhost);
+ printerr(1, "%s(0x%0lx): inhost '%s' different than outhost '%s'\n",
+ __func__, tid, inhost, outhost);
retval = 0;
out:

View File

@ -0,0 +1,625 @@
diff -up nfs-utils-2.3.3/nfs.conf.orig nfs-utils-2.3.3/nfs.conf
--- nfs-utils-2.3.3/nfs.conf.orig 2021-07-19 09:45:40.441448059 -0400
+++ nfs-utils-2.3.3/nfs.conf 2021-07-19 12:08:55.314182838 -0400
@@ -22,6 +22,8 @@ use-gss-proxy=1
# cred-cache-directory=
# preferred-realm=
# set-home=1
+# upcall-timeout=30
+# cancel-timed-out-upcalls=0
#
[lockd]
# port=0
diff -up nfs-utils-2.3.3/utils/gssd/gssd.c.orig nfs-utils-2.3.3/utils/gssd/gssd.c
--- nfs-utils-2.3.3/utils/gssd/gssd.c.orig 2021-07-19 09:45:40.448448246 -0400
+++ nfs-utils-2.3.3/utils/gssd/gssd.c 2021-07-19 12:08:55.315182865 -0400
@@ -96,8 +96,29 @@ pthread_mutex_t clp_lock = PTHREAD_MUTEX
static bool signal_received = false;
static struct event_base *evbase = NULL;
+int upcall_timeout = DEF_UPCALL_TIMEOUT;
+static bool cancel_timed_out_upcalls = false;
+
TAILQ_HEAD(topdir_list_head, topdir) topdir_list;
+/*
+ * active_thread_list:
+ *
+ * used to track upcalls for timeout purposes.
+ *
+ * protected by the active_thread_list_lock mutex.
+ *
+ * upcall_thread_info structures are added to the tail of the list
+ * by start_upcall_thread(), so entries closer to the head of the list
+ * will be closer to hitting the upcall timeout.
+ *
+ * upcall_thread_info structures are removed from the list upon a
+ * sucessful join of the upcall thread by the watchdog thread (via
+ * scan_active_thread_list().
+ */
+TAILQ_HEAD(active_thread_list_head, upcall_thread_info) active_thread_list;
+pthread_mutex_t active_thread_list_lock = PTHREAD_MUTEX_INITIALIZER;
+
struct topdir {
TAILQ_ENTRY(topdir) list;
TAILQ_HEAD(clnt_list_head, clnt_info) clnt_list;
@@ -436,6 +457,138 @@ gssd_clnt_krb5_cb(int UNUSED(fd), short
handle_krb5_upcall(clp);
}
+/*
+ * scan_active_thread_list:
+ *
+ * Walks the active_thread_list, trying to join as many upcall threads as
+ * possible. For threads that have terminated, the corresponding
+ * upcall_thread_info will be removed from the list and freed. Threads that
+ * are still busy and have exceeded the upcall_timeout will cause an error to
+ * be logged and may be canceled (depending on the value of
+ * cancel_timed_out_upcalls).
+ *
+ * Returns the number of seconds that the watchdog thread should wait before
+ * calling scan_active_thread_list() again.
+ */
+static int
+scan_active_thread_list(void)
+{
+ struct upcall_thread_info *info;
+ struct timespec now;
+ unsigned int sleeptime;
+ bool sleeptime_set = false;
+ int err;
+ void *tret, *saveprev;
+
+ sleeptime = upcall_timeout;
+ pthread_mutex_lock(&active_thread_list_lock);
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ TAILQ_FOREACH(info, &active_thread_list, list) {
+ err = pthread_tryjoin_np(info->tid, &tret);
+ switch (err) {
+ case 0:
+ /*
+ * The upcall thread has either completed successfully, or
+ * has been canceled _and_ has acted on the cancellation request
+ * (i.e. has hit a cancellation point). We can now remove the
+ * upcall_thread_info from the list and free it.
+ */
+ if (tret == PTHREAD_CANCELED)
+ printerr(3, "watchdog: thread id 0x%lx cancelled successfully\n",
+ info->tid);
+ saveprev = info->list.tqe_prev;
+ TAILQ_REMOVE(&active_thread_list, info, list);
+ free(info);
+ info = saveprev;
+ break;
+ case EBUSY:
+ /*
+ * The upcall thread is still running. If the timeout has expired
+ * then we either cancel the thread, log an error, and do an error
+ * downcall to the kernel (cancel_timed_out_upcalls=true) or simply
+ * log an error (cancel_timed_out_upcalls=false). In either case,
+ * the error is logged only once.
+ */
+ if (now.tv_sec >= info->timeout.tv_sec) {
+ if (cancel_timed_out_upcalls && !(info->flags & UPCALL_THREAD_CANCELED)) {
+ printerr(0, "watchdog: thread id 0x%lx timed out\n",
+ info->tid);
+ pthread_cancel(info->tid);
+ info->flags |= (UPCALL_THREAD_CANCELED|UPCALL_THREAD_WARNED);
+ do_error_downcall(info->fd, info->uid, -ETIMEDOUT);
+ } else {
+ if (!(info->flags & UPCALL_THREAD_WARNED)) {
+ printerr(0, "watchdog: thread id 0x%lx running for %ld seconds\n",
+ info->tid,
+ now.tv_sec - info->timeout.tv_sec + upcall_timeout);
+ info->flags |= UPCALL_THREAD_WARNED;
+ }
+ }
+ } else if (!sleeptime_set) {
+ /*
+ * The upcall thread is still running, but the timeout has not yet
+ * expired. Calculate the time remaining until the timeout will
+ * expire. This is the amount of time the watchdog thread will
+ * wait before running again. We only need to do this for the busy
+ * thread closest to the head of the list - entries appearing later
+ * in the list will time out later.
+ */
+ sleeptime = info->timeout.tv_sec - now.tv_sec;
+ sleeptime_set = true;
+ }
+ break;
+ default:
+ /* EDEADLK, EINVAL, and ESRCH... none of which should happen! */
+ printerr(0, "watchdog: attempt to join thread id 0x%lx returned %d (%s)!\n",
+ info->tid, err, strerror(err));
+ break;
+ }
+ }
+ pthread_mutex_unlock(&active_thread_list_lock);
+
+ return sleeptime;
+}
+
+static void *
+watchdog_thread_fn(void *UNUSED(arg))
+{
+ unsigned int sleeptime;
+
+ for (;;) {
+ sleeptime = scan_active_thread_list();
+ printerr(4, "watchdog: sleeping %u secs\n", sleeptime);
+ sleep(sleeptime);
+ }
+ return (void *)0;
+}
+
+static int
+start_watchdog_thread(void)
+{
+ pthread_attr_t attr;
+ pthread_t th;
+ int ret;
+
+ ret = pthread_attr_init(&attr);
+ if (ret != 0) {
+ printerr(0, "ERROR: failed to init pthread attr: ret %d: %s\n",
+ ret, strerror(errno));
+ return ret;
+ }
+ ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (ret != 0) {
+ printerr(0, "ERROR: failed to create pthread attr: ret %d: %s\n",
+ ret, strerror(errno));
+ return ret;
+ }
+ ret = pthread_create(&th, &attr, watchdog_thread_fn, NULL);
+ if (ret != 0) {
+ printerr(0, "ERROR: pthread_create failed: ret %d: %s\n",
+ ret, strerror(errno));
+ }
+ return ret;
+}
+
static struct clnt_info *
gssd_get_clnt(struct topdir *tdi, const char *name)
{
@@ -810,7 +963,7 @@ sig_die(int signal)
static void
usage(char *progname)
{
- fprintf(stderr, "usage: %s [-f] [-l] [-M] [-n] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir] [-t timeout] [-R preferred realm] [-D] [-H]\n",
+ fprintf(stderr, "usage: %s [-f] [-l] [-M] [-n] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir] [-t timeout] [-R preferred realm] [-D] [-H] [-U upcall timeout] [-C]\n",
progname);
exit(1);
}
@@ -831,6 +984,9 @@ read_gss_conf(void)
#endif
context_timeout = conf_get_num("gssd", "context-timeout", context_timeout);
rpc_timeout = conf_get_num("gssd", "rpc-timeout", rpc_timeout);
+ upcall_timeout = conf_get_num("gssd", "upcall-timeout", upcall_timeout);
+ cancel_timed_out_upcalls = conf_get_bool("gssd", "cancel-timed-out-upcalls",
+ cancel_timed_out_upcalls);
s = conf_get_str("gssd", "pipefs-directory");
if (!s)
s = conf_get_str("general", "pipefs-directory");
@@ -872,7 +1028,7 @@ main(int argc, char *argv[])
verbosity = conf_get_num("gssd", "verbosity", verbosity);
rpc_verbosity = conf_get_num("gssd", "rpc-verbosity", rpc_verbosity);
- while ((opt = getopt(argc, argv, "HDfvrlmnMp:k:d:t:T:R:")) != -1) {
+ while ((opt = getopt(argc, argv, "HDfvrlmnMp:k:d:t:T:R:U:C")) != -1) {
switch (opt) {
case 'f':
fg = 1;
@@ -923,6 +1079,12 @@ main(int argc, char *argv[])
case 'H':
set_home = false;
break;
+ case 'U':
+ upcall_timeout = atoi(optarg);
+ break;
+ case 'C':
+ cancel_timed_out_upcalls = true;
+ break;
default:
usage(argv[0]);
break;
@@ -995,6 +1157,11 @@ main(int argc, char *argv[])
else
progname = argv[0];
+ if (upcall_timeout > MAX_UPCALL_TIMEOUT)
+ upcall_timeout = MAX_UPCALL_TIMEOUT;
+ else if (upcall_timeout < MIN_UPCALL_TIMEOUT)
+ upcall_timeout = MIN_UPCALL_TIMEOUT;
+
initerr(progname, verbosity, fg);
#ifdef HAVE_LIBTIRPC_SET_DEBUG
/*
@@ -1045,6 +1212,14 @@ main(int argc, char *argv[])
gssd_inotify_cb, NULL);
event_add(inotify_ev, NULL);
+ TAILQ_INIT(&active_thread_list);
+
+ rc = start_watchdog_thread();
+ if (rc != 0) {
+ printerr(0, "ERROR: failed to start watchdog thread: %d\n", rc);
+ exit(EXIT_FAILURE);
+ }
+
TAILQ_INIT(&topdir_list);
gssd_scan();
daemon_ready();
diff -up nfs-utils-2.3.3/utils/gssd/gssd.h.orig nfs-utils-2.3.3/utils/gssd/gssd.h
--- nfs-utils-2.3.3/utils/gssd/gssd.h.orig 2021-07-19 09:45:40.449448272 -0400
+++ nfs-utils-2.3.3/utils/gssd/gssd.h 2021-07-19 12:08:55.315182865 -0400
@@ -50,6 +50,12 @@
#define GSSD_DEFAULT_KEYTAB_FILE "/etc/krb5.keytab"
#define GSSD_SERVICE_NAME "nfs"
#define RPC_CHAN_BUF_SIZE 32768
+
+/* timeouts are in seconds */
+#define MIN_UPCALL_TIMEOUT 5
+#define DEF_UPCALL_TIMEOUT 30
+#define MAX_UPCALL_TIMEOUT 600
+
/*
* The gss mechanisms that we can handle
*/
@@ -91,10 +97,22 @@ struct clnt_upcall_info {
char *service;
};
+struct upcall_thread_info {
+ TAILQ_ENTRY(upcall_thread_info) list;
+ pthread_t tid;
+ struct timespec timeout;
+ uid_t uid;
+ int fd;
+ unsigned short flags;
+#define UPCALL_THREAD_CANCELED 0x0001
+#define UPCALL_THREAD_WARNED 0x0002
+};
+
void handle_krb5_upcall(struct clnt_info *clp);
void handle_gssd_upcall(struct clnt_info *clp);
void free_upcall_info(struct clnt_upcall_info *info);
void gssd_free_client(struct clnt_info *clp);
+int do_error_downcall(int k5_fd, uid_t uid, int err);
#endif /* _RPC_GSSD_H_ */
diff -up nfs-utils-2.3.3/utils/gssd/gssd.man.orig nfs-utils-2.3.3/utils/gssd/gssd.man
--- nfs-utils-2.3.3/utils/gssd/gssd.man.orig 2021-07-19 09:45:40.443448112 -0400
+++ nfs-utils-2.3.3/utils/gssd/gssd.man 2021-07-19 12:08:55.315182865 -0400
@@ -8,7 +8,7 @@
rpc.gssd \- RPCSEC_GSS daemon
.SH SYNOPSIS
.B rpc.gssd
-.RB [ \-DfMnlvrH ]
+.RB [ \-DfMnlvrHC ]
.RB [ \-k
.IR keytab ]
.RB [ \-p
@@ -17,6 +17,10 @@ rpc.gssd \- RPCSEC_GSS daemon
.IR ccachedir ]
.RB [ \-t
.IR timeout ]
+.RB [ \-T
+.IR timeout ]
+.RB [ \-U
+.IR timeout ]
.RB [ \-R
.IR realm ]
.SH INTRODUCTION
@@ -290,7 +294,7 @@ seconds, which allows changing Kerberos
The default is no explicit timeout, which means the kernel context will live
the lifetime of the Kerberos service ticket used in its creation.
.TP
-.B -T timeout
+.BI "-T " timeout
Timeout, in seconds, to create an RPC connection with a server while
establishing an authenticated gss context for a user.
The default timeout is set to 5 seconds.
@@ -298,6 +302,18 @@ If you get messages like "WARNING: can't
%servername% for user with uid %uid%: RPC: Remote system error -
Connection timed out", you should consider an increase of this timeout.
.TP
+.BI "-U " timeout
+Timeout, in seconds, for upcall threads. Threads executing longer than
+.I timeout
+seconds will cause an error message to be logged. The default
+.I timeout
+is 30 seconds. The minimum is 5 seconds. The maximum is 600 seconds.
+.TP
+.B -C
+In addition to logging an error message for threads that have timed out,
+the thread will be canceled and an error of -ETIMEDOUT will be reported
+to the kernel.
+.TP
.B -H
Avoids setting $HOME to "/". This allows rpc.gssd to read per user k5identity
files versus trying to read /.k5identity for each user.
@@ -365,6 +381,17 @@ Equivalent to
Equivalent to
.BR -R .
.TP
+.B upcall-timeout
+Equivalent to
+.BR -U .
+.TP
+.B cancel-timed-out-upcalls
+Setting to
+.B true
+is equivalent to providing the
+.B -C
+flag.
+.TP
.B set-home
Setting to
.B false
diff -up nfs-utils-2.3.3/utils/gssd/gssd_proc.c.orig nfs-utils-2.3.3/utils/gssd/gssd_proc.c
--- nfs-utils-2.3.3/utils/gssd/gssd_proc.c.orig 2021-07-19 09:45:40.449448272 -0400
+++ nfs-utils-2.3.3/utils/gssd/gssd_proc.c 2021-07-19 12:08:55.316182891 -0400
@@ -81,11 +81,24 @@
#include "gss_names.h"
extern pthread_mutex_t clp_lock;
+extern pthread_mutex_t active_thread_list_lock;
+extern int upcall_timeout;
+extern TAILQ_HEAD(active_thread_list_head, upcall_thread_info) active_thread_list;
/* Encryption types supported by the kernel rpcsec_gss code */
int num_krb5_enctypes = 0;
krb5_enctype *krb5_enctypes = NULL;
+/* Args for the cleanup_handler() */
+struct cleanup_args {
+ OM_uint32 *min_stat;
+ gss_buffer_t acceptor;
+ gss_buffer_t token;
+ struct authgss_private_data *pd;
+ AUTH **auth;
+ CLIENT **rpc_clnt;
+};
+
/*
* Parse the supported encryption type information
*/
@@ -184,7 +197,7 @@ out_err:
return;
}
-static int
+int
do_error_downcall(int k5_fd, uid_t uid, int err)
{
char buf[1024];
@@ -604,27 +617,66 @@ out:
}
/*
+ * cleanup_handler:
+ *
+ * Free any resources allocated by process_krb5_upcall().
+ *
+ * Runs upon normal termination of process_krb5_upcall as well as if the
+ * thread is canceled.
+ */
+static void
+cleanup_handler(void *arg)
+{
+ struct cleanup_args *args = (struct cleanup_args *)arg;
+
+ gss_release_buffer(args->min_stat, args->acceptor);
+ if (args->token->value)
+ free(args->token->value);
+#ifdef HAVE_AUTHGSS_FREE_PRIVATE_DATA
+ if (args->pd->pd_ctx_hndl.length != 0 || args->pd->pd_ctx != 0)
+ authgss_free_private_data(args->pd);
+#endif
+ if (*args->auth)
+ AUTH_DESTROY(*args->auth);
+ if (*args->rpc_clnt)
+ clnt_destroy(*args->rpc_clnt);
+}
+
+/*
+ * process_krb5_upcall:
+ *
* this code uses the userland rpcsec gss library to create a krb5
* context on behalf of the kernel
+ *
+ * This is the meat of the upcall thread. Note that cancelability is disabled
+ * and enabled at various points to ensure that any resources reserved by the
+ * lower level libraries are released safely.
*/
static void
-process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *srchost,
- char *tgtname, char *service)
+process_krb5_upcall(struct clnt_upcall_info *info)
{
+ struct clnt_info *clp = info->clp;
+ uid_t uid = info->uid;
+ int fd = info->fd;
+ char *srchost = info->srchost;
+ char *tgtname = info->target;
+ char *service = info->service;
CLIENT *rpc_clnt = NULL;
AUTH *auth = NULL;
struct authgss_private_data pd;
gss_buffer_desc token;
- int err, downcall_err = -EACCES;
+ int err, downcall_err;
OM_uint32 maj_stat, min_stat, lifetime_rec;
gss_name_t gacceptor = GSS_C_NO_NAME;
gss_OID mech;
gss_buffer_desc acceptor = {0};
+ struct cleanup_args cleanup_args = {&min_stat, &acceptor, &token, &pd, &auth, &rpc_clnt};
token.length = 0;
token.value = NULL;
memset(&pd, 0, sizeof(struct authgss_private_data));
+ pthread_cleanup_push(cleanup_handler, &cleanup_args);
/*
* If "service" is specified, then the kernel is indicating that
* we must use machine credentials for this request. (Regardless
@@ -646,6 +698,8 @@ process_krb5_upcall(struct clnt_info *cl
* used for this case is not important.
*
*/
+ downcall_err = -EACCES;
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0 &&
service == NULL)) {
@@ -666,15 +720,21 @@ process_krb5_upcall(struct clnt_info *cl
goto out_return_error;
}
}
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ pthread_testcancel();
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
if (!authgss_get_private_data(auth, &pd)) {
printerr(1, "WARNING: Failed to obtain authentication "
"data for user with uid %d for server %s\n",
uid, clp->servername);
goto out_return_error;
}
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ pthread_testcancel();
/* Grab the context lifetime and acceptor name out of the ctx. */
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
maj_stat = gss_inquire_context(&min_stat, pd.pd_ctx, NULL, &gacceptor,
&lifetime_rec, &mech, NULL, NULL, NULL);
@@ -686,37 +746,35 @@ process_krb5_upcall(struct clnt_info *cl
get_hostbased_client_buffer(gacceptor, mech, &acceptor);
gss_release_name(&min_stat, &gacceptor);
}
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ pthread_testcancel();
/*
* The serialization can mean turning pd.pd_ctx into a lucid context. If
* that happens then the pd.pd_ctx will be unusable, so we must never
* try to use it after this point.
*/
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
if (serialize_context_for_kernel(&pd.pd_ctx, &token, &krb5oid, NULL)) {
printerr(1, "WARNING: Failed to serialize krb5 context for "
"user with uid %d for server %s\n",
uid, clp->servername);
goto out_return_error;
}
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ pthread_testcancel();
do_downcall(fd, uid, &pd, &token, lifetime_rec, &acceptor);
out:
- gss_release_buffer(&min_stat, &acceptor);
- if (token.value)
- free(token.value);
-#ifdef HAVE_AUTHGSS_FREE_PRIVATE_DATA
- if (pd.pd_ctx_hndl.length != 0 || pd.pd_ctx != 0)
- authgss_free_private_data(&pd);
-#endif
- if (auth)
- AUTH_DESTROY(auth);
- if (rpc_clnt)
- clnt_destroy(rpc_clnt);
+ pthread_cleanup_pop(1);
return;
out_return_error:
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ pthread_testcancel();
+
do_error_downcall(fd, uid, downcall_err);
goto out;
}
@@ -782,36 +840,69 @@ void free_upcall_info(struct clnt_upcall
}
static void
-gssd_work_thread_fn(struct clnt_upcall_info *info)
+cleanup_clnt_upcall_info(void *arg)
{
- process_krb5_upcall(info->clp, info->uid, info->fd, info->srchost, info->target, info->service);
+ struct clnt_upcall_info *info = (struct clnt_upcall_info *)arg;
+
free_upcall_info(info);
}
+static void
+gssd_work_thread_fn(struct clnt_upcall_info *info)
+{
+ pthread_cleanup_push(cleanup_clnt_upcall_info, info);
+ process_krb5_upcall(info);
+ pthread_cleanup_pop(1);
+}
+
+static struct upcall_thread_info *
+alloc_upcall_thread_info(void)
+{
+ struct upcall_thread_info *info;
+
+ info = malloc(sizeof(struct upcall_thread_info));
+ if (info == NULL)
+ return NULL;
+ memset(info, 0, sizeof(*info));
+ return info;
+}
+
static int
-start_upcall_thread(void (*func)(struct clnt_upcall_info *), void *info)
+start_upcall_thread(void (*func)(struct clnt_upcall_info *), struct clnt_upcall_info *info)
{
pthread_attr_t attr;
pthread_t th;
+ struct upcall_thread_info *tinfo;
int ret;
+ tinfo = alloc_upcall_thread_info();
+ if (!tinfo)
+ return -ENOMEM;
+ tinfo->fd = info->fd;
+ tinfo->uid = info->uid;
+
ret = pthread_attr_init(&attr);
if (ret != 0) {
printerr(0, "ERROR: failed to init pthread attr: ret %d: %s\n",
ret, strerror(errno));
- return ret;
- }
- ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
- if (ret != 0) {
- printerr(0, "ERROR: failed to create pthread attr: ret %d: "
- "%s\n", ret, strerror(errno));
+ free(tinfo);
return ret;
}
ret = pthread_create(&th, &attr, (void *)func, (void *)info);
- if (ret != 0)
+ if (ret != 0) {
printerr(0, "ERROR: pthread_create failed: ret %d: %s\n",
ret, strerror(errno));
+ free(tinfo);
+ return ret;
+ }
+ tinfo->tid = th;
+ pthread_mutex_lock(&active_thread_list_lock);
+ clock_gettime(CLOCK_MONOTONIC, &tinfo->timeout);
+ tinfo->timeout.tv_sec += upcall_timeout;
+ TAILQ_INSERT_TAIL(&active_thread_list, tinfo, list);
+ pthread_mutex_unlock(&active_thread_list_lock);
+
return ret;
}

View File

@ -0,0 +1,14 @@
diff -up nfs-utils-2.3.3/utils/mount/stropts.c.orig nfs-utils-2.3.3/utils/mount/stropts.c
--- nfs-utils-2.3.3/utils/mount/stropts.c.orig 2022-02-14 11:28:51.570084952 -0500
+++ nfs-utils-2.3.3/utils/mount/stropts.c 2022-02-14 11:29:16.174450628 -0500
@@ -966,7 +966,9 @@ fall_back:
if ((result = nfs_try_mount_v3v2(mi, FALSE)))
return result;
- errno = olderrno;
+ if (errno != EBUSY && errno != EACCES)
+ errno = olderrno;
+
return result;
}

View File

@ -0,0 +1,116 @@
diff -up nfs-utils-2.3.3/utils/mount/nfs.man.save nfs-utils-2.3.3/utils/mount/nfs.man
--- nfs-utils-2.3.3/utils/mount/nfs.man.save 2021-07-28 14:42:20.977740892 -0400
+++ nfs-utils-2.3.3/utils/mount/nfs.man 2021-07-28 14:42:01.133212815 -0400
@@ -525,6 +525,13 @@ using the FS-Cache facility. See cachefi
and <kernel_soruce>/Documentation/filesystems/caching
for detail on how to configure the FS-Cache facility.
Default value is nofsc.
+.TP 1.5i
+.B sloppy
+The
+.B sloppy
+option is an alternative to specifying
+.BR mount.nfs " -s " option.
+
.SS "Options for NFS versions 2 and 3 only"
Use these options, along with the options in the above subsection,
for NFS versions 2 and 3 only.
diff -up nfs-utils-2.3.3/utils/mount/parse_opt.c.save nfs-utils-2.3.3/utils/mount/parse_opt.c
--- nfs-utils-2.3.3/utils/mount/parse_opt.c.save 2021-07-28 14:40:15.467400995 -0400
+++ nfs-utils-2.3.3/utils/mount/parse_opt.c 2021-07-28 14:39:57.666927309 -0400
@@ -178,6 +178,22 @@ static void options_tail_insert(struct m
options->count++;
}
+static void options_head_insert(struct mount_options *options,
+ struct mount_option *option)
+{
+ struct mount_option *ohead = options->head;
+
+ option->prev = NULL;
+ option->next = ohead;
+ if (ohead)
+ ohead->prev = option;
+ else
+ options->tail = option;
+ options->head = option;
+
+ options->count++;
+}
+
static void options_delete(struct mount_options *options,
struct mount_option *option)
{
@@ -374,6 +390,23 @@ po_return_t po_join(struct mount_options
}
/**
+ * po_insert - insert an option into a group of options
+ * @options: pointer to mount options
+ * @option: pointer to a C string containing the option to add
+ *
+ */
+po_return_t po_insert(struct mount_options *options, char *str)
+{
+ struct mount_option *option = option_create(str);
+
+ if (option) {
+ options_head_insert(options, option);
+ return PO_SUCCEEDED;
+ }
+ return PO_FAILED;
+}
+
+/**
* po_append - concatenate an option onto a group of options
* @options: pointer to mount options
* @option: pointer to a C string containing the option to add
diff -up nfs-utils-2.3.3/utils/mount/parse_opt.h.save nfs-utils-2.3.3/utils/mount/parse_opt.h
--- nfs-utils-2.3.3/utils/mount/parse_opt.h.save 2021-07-28 14:40:54.292434148 -0400
+++ nfs-utils-2.3.3/utils/mount/parse_opt.h 2021-07-28 14:39:57.666927309 -0400
@@ -43,6 +43,7 @@ void po_replace(struct mount_options *
struct mount_options *);
po_return_t po_join(struct mount_options *, char **);
+po_return_t po_insert(struct mount_options *, char *);
po_return_t po_append(struct mount_options *, char *);
po_found_t po_contains(struct mount_options *, char *);
po_found_t po_contains_prefix(struct mount_options *options,
diff -up nfs-utils-2.3.3/utils/mount/stropts.c.save nfs-utils-2.3.3/utils/mount/stropts.c
--- nfs-utils-2.3.3/utils/mount/stropts.c.save 2021-07-28 14:41:14.842981010 -0400
+++ nfs-utils-2.3.3/utils/mount/stropts.c 2021-07-28 14:42:01.134212842 -0400
@@ -336,13 +336,21 @@ static int nfs_verify_lock_option(struct
return 1;
}
-static int nfs_append_sloppy_option(struct mount_options *options)
+static int nfs_insert_sloppy_option(struct mount_options *options)
{
- if (!sloppy || linux_version_code() < MAKE_VERSION(2, 6, 27))
+ if (linux_version_code() < MAKE_VERSION(2, 6, 27))
return 1;
- if (po_append(options, "sloppy") == PO_FAILED)
- return 0;
+ if (po_contains(options, "sloppy")) {
+ po_remove_all(options, "sloppy");
+ sloppy++;
+ }
+
+ if (sloppy) {
+ if (po_insert(options, "sloppy") == PO_FAILED)
+ return 0;
+ }
+
return 1;
}
@@ -424,7 +432,7 @@ static int nfs_validate_options(struct n
if (!nfs_set_version(mi))
return 0;
- if (!nfs_append_sloppy_option(mi->options))
+ if (!nfs_insert_sloppy_option(mi->options))
return 0;
return 1;

View File

@ -0,0 +1,61 @@
diff --git a/utils/mountd/v4root.c b/utils/mountd/v4root.c
index d735dbfe..8ec33fb0 100644
--- a/utils/mountd/v4root.c
+++ b/utils/mountd/v4root.c
@@ -36,9 +36,9 @@ static nfs_export pseudo_root = {
.m_export = {
.e_hostname = "*",
.e_path = "/",
- .e_flags = NFSEXP_READONLY | NFSEXP_ROOTSQUASH
+ .e_flags = NFSEXP_READONLY
| NFSEXP_NOSUBTREECHECK | NFSEXP_FSID
- | NFSEXP_V4ROOT,
+ | NFSEXP_V4ROOT | NFSEXP_INSECURE_PORT,
.e_anonuid = 65534,
.e_anongid = 65534,
.e_squids = NULL,
@@ -57,15 +57,11 @@ static nfs_export pseudo_root = {
};
static void
-set_pseudofs_security(struct exportent *pseudo, int flags)
+set_pseudofs_security(struct exportent *pseudo)
{
struct flav_info *flav;
int i;
- if (flags & NFSEXP_INSECURE_PORT)
- pseudo->e_flags |= NFSEXP_INSECURE_PORT;
- if ((flags & NFSEXP_ROOTSQUASH) == 0)
- pseudo->e_flags &= ~NFSEXP_ROOTSQUASH;
for (flav = flav_map; flav < flav_map + flav_map_size; flav++) {
struct sec_entry *new;
@@ -75,8 +71,7 @@ set_pseudofs_security(struct exportent *pseudo, int flags)
i = secinfo_addflavor(flav, pseudo);
new = &pseudo->e_secinfo[i];
- if (flags & NFSEXP_INSECURE_PORT)
- new->flags |= NFSEXP_INSECURE_PORT;
+ new->flags |= NFSEXP_INSECURE_PORT;
}
}
@@ -95,7 +90,7 @@ v4root_create(char *path, nfs_export *export)
strncpy(eep.e_path, path, sizeof(eep.e_path)-1);
if (strcmp(path, "/") != 0)
eep.e_flags &= ~NFSEXP_FSID;
- set_pseudofs_security(&eep, curexp->e_flags);
+ set_pseudofs_security(&eep);
exp = export_create(&eep, 0);
if (exp == NULL)
return NULL;
@@ -143,7 +138,7 @@ pseudofs_update(char *hostname, char *path, nfs_export *source)
return 0;
}
/* Update an existing V4ROOT export: */
- set_pseudofs_security(&exp->m_export, source->m_export.e_flags);
+ set_pseudofs_security(&exp->m_export);
return 0;
}

View File

@ -0,0 +1,976 @@
diff --git a/nfs.conf b/nfs.conf
index 05247ff9..86ed7d53 100644
--- a/nfs.conf
+++ b/nfs.conf
@@ -38,6 +38,8 @@ use-gss-proxy=1
# reverse-lookup=n
# state-directory-path=/var/lib/nfs
# ha-callout=
+# cache-use-ipaddr=n
+# ttl=1800
#
[nfsdcld]
# debug=0
diff --git a/support/export/Makefile.am b/support/export/Makefile.am
index 13f7a49c..d6ee502f 100644
--- a/support/export/Makefile.am
+++ b/support/export/Makefile.am
@@ -11,7 +11,8 @@ EXTRA_DIST = mount.x
noinst_LIBRARIES = libexport.a
libexport_a_SOURCES = client.c export.c hostname.c \
- xtab.c mount_clnt.c mount_xdr.c
+ xtab.c mount_clnt.c mount_xdr.c \
+ cache.c auth.c v4root.c v4clients.c
BUILT_SOURCES = $(GENFILES)
noinst_HEADERS = mount.h
diff --git a/utils/mountd/auth.c b/support/export/auth.c
similarity index 98%
rename from utils/mountd/auth.c
rename to support/export/auth.c
index 8299256e..73ad6f73 100644
--- a/utils/mountd/auth.c
+++ b/support/export/auth.c
@@ -22,7 +22,7 @@
#include "misc.h"
#include "nfslib.h"
#include "exportfs.h"
-#include "mountd.h"
+#include "export.h"
#include "v4root.h"
enum auth_error
@@ -43,11 +43,13 @@ extern int use_ipaddr;
extern struct state_paths etab;
+/*
void
auth_init(void)
{
auth_reload();
}
+*/
/*
* A client can match many different netgroups and it's tough to know
@@ -64,6 +66,10 @@ check_useipaddr(void)
int old_use_ipaddr = use_ipaddr;
unsigned int len = 0;
+ if (use_ipaddr > 1)
+ /* fixed - don't check */
+ return;
+
/* add length of m_hostname + 1 for the comma */
for (clp = clientlist[MCL_NETGROUP]; clp; clp = clp->m_next)
len += (strlen(clp->m_hostname) + 1);
diff --git a/utils/mountd/cache.c b/support/export/cache.c
similarity index 95%
rename from utils/mountd/cache.c
rename to support/export/cache.c
index c73e29be..98d50828 100644
--- a/utils/mountd/cache.c
+++ b/support/export/cache.c
@@ -29,21 +29,18 @@
#include "misc.h"
#include "nfslib.h"
#include "exportfs.h"
-#include "mountd.h"
-#include "fsloc.h"
+#include "export.h"
#include "pseudoflavors.h"
#include "xcommon.h"
+#ifdef HAVE_JUNCTION_SUPPORT
+#include "../../utils/mountd/fsloc.h"
+#endif
+
#ifdef USE_BLKID
#include "blkid/blkid.h"
#endif
-/*
- * Invoked by RPC service loop
- */
-void cache_set_fds(fd_set *fdset);
-int cache_process_req(fd_set *readfds);
-
enum nfsd_fsid {
FSID_DEV = 0,
FSID_NUM,
@@ -63,7 +60,6 @@ enum nfsd_fsid {
* Record is terminated with newline.
*
*/
-static int cache_export_ent(char *buf, int buflen, char *domain, struct exportent *exp, char *path);
#define INITIAL_MANAGED_GROUPS 100
@@ -81,6 +77,7 @@ static void auth_unix_ip(int f)
char class[20];
char ipaddr[INET6_ADDRSTRLEN + 1];
char *client = NULL;
+ struct addrinfo *ai = NULL;
struct addrinfo *tmp = NULL;
char buf[RPC_CHAN_BUF_SIZE], *bp;
int blen;
@@ -106,21 +103,26 @@ static void auth_unix_ip(int f)
auth_reload();
- /* addr is a valid, interesting address, find the domain name... */
- if (!use_ipaddr) {
- struct addrinfo *ai = NULL;
-
- ai = client_resolve(tmp->ai_addr);
- if (ai) {
- client = client_compose(ai);
- freeaddrinfo(ai);
- }
+ /* addr is a valid address, find the domain name... */
+ ai = client_resolve(tmp->ai_addr);
+ if (ai) {
+ client = client_compose(ai);
+ freeaddrinfo(ai);
}
+ if (!client)
+ xlog(D_AUTH, "failed authentication for IP %s", ipaddr);
+ else if (!use_ipaddr)
+ xlog(D_AUTH, "successful authentication for IP %s as %s",
+ ipaddr, *client ? client : "DEFAULT");
+ else
+ xlog(D_AUTH, "successful authentication for IP %s",
+ ipaddr);
+
bp = buf; blen = sizeof(buf);
qword_add(&bp, &blen, "nfsd");
qword_add(&bp, &blen, ipaddr);
- qword_adduint(&bp, &blen, time(0) + DEFAULT_TTL);
- if (use_ipaddr) {
+ qword_adduint(&bp, &blen, time(0) + default_ttl);
+ if (use_ipaddr && client) {
memmove(ipaddr + 1, ipaddr, strlen(ipaddr) + 1);
ipaddr[0] = '$';
qword_add(&bp, &blen, ipaddr);
@@ -192,7 +194,7 @@ static void auth_unix_gid(int f)
bp = buf; blen = sizeof(buf);
qword_adduint(&bp, &blen, uid);
- qword_adduint(&bp, &blen, time(0) + DEFAULT_TTL);
+ qword_adduint(&bp, &blen, time(0) + default_ttl);
if (rv >= 0) {
qword_adduint(&bp, &blen, ngroups);
for (i=0; i<ngroups; i++)
@@ -688,7 +690,6 @@ static void nfsd_fh(int f)
char *found_path = NULL;
nfs_export *exp;
int i;
- int dev_missing = 0;
char buf[RPC_CHAN_BUF_SIZE], *bp;
int blen;
@@ -755,11 +756,6 @@ static void nfsd_fh(int f)
if (!is_ipaddr_client(dom)
&& !namelist_client_matches(exp, dom))
continue;
- if (exp->m_export.e_mountpoint &&
- !is_mountpoint(exp->m_export.e_mountpoint[0]?
- exp->m_export.e_mountpoint:
- exp->m_export.e_path))
- dev_missing ++;
if (!match_fsid(&parsed, exp, path))
continue;
@@ -794,7 +790,7 @@ static void nfsd_fh(int f)
!is_mountpoint(found->e_mountpoint[0]?
found->e_mountpoint:
found->e_path)) {
- /* Cannot export this yet
+ /* Cannot export this yet
* should log a warning, but need to rate limit
xlog(L_WARNING, "%s not exported as %d not a mountpoint",
found->e_path, found->e_mountpoint);
@@ -802,16 +798,6 @@ static void nfsd_fh(int f)
/* FIXME we need to make sure we re-visit this later */
goto out;
}
- if (!found && dev_missing) {
- /* The missing dev could be what we want, so just be
- * quite rather than returning stale yet
- */
- goto out;
- }
-
- if (found)
- if (cache_export_ent(buf, sizeof(buf), dom, found, found_path) < 0)
- found = 0;
bp = buf; blen = sizeof(buf);
qword_add(&bp, &blen, dom);
@@ -831,6 +817,8 @@ static void nfsd_fh(int f)
qword_addeol(&bp, &blen);
if (blen <= 0 || write(f, buf, bp - buf) != bp - buf)
xlog(L_ERROR, "nfsd_fh: error writing reply");
+ if (!found)
+ xlog(D_AUTH, "denied access to %s", *dom == '$' ? dom+1 : dom);
out:
if (found_path)
free(found_path);
@@ -839,6 +827,7 @@ out:
xlog(D_CALL, "nfsd_fh: found %p path %s", found, found ? found->e_path : NULL);
}
+#ifdef HAVE_JUNCTION_SUPPORT
static void write_fsloc(char **bp, int *blen, struct exportent *ep)
{
struct servers *servers;
@@ -861,7 +850,7 @@ static void write_fsloc(char **bp, int *blen, struct exportent *ep)
qword_addint(bp, blen, servers->h_referral);
release_replicas(servers);
}
-
+#endif
static void write_secinfo(char **bp, int *blen, struct exportent *ep, int flag_mask)
{
struct sec_entry *p;
@@ -890,7 +879,7 @@ static int dump_to_cache(int f, char *buf, int buflen, char *domain,
time_t now = time(0);
if (ttl <= 1)
- ttl = DEFAULT_TTL;
+ ttl = default_ttl;
qword_add(&bp, &blen, domain);
qword_add(&bp, &blen, path);
@@ -903,7 +892,10 @@ static int dump_to_cache(int f, char *buf, int buflen, char *domain,
qword_addint(&bp, &blen, exp->e_anonuid);
qword_addint(&bp, &blen, exp->e_anongid);
qword_addint(&bp, &blen, exp->e_fsid);
+
+#ifdef HAVE_JUNCTION_SUPPORT
write_fsloc(&bp, &blen, exp);
+#endif
write_secinfo(&bp, &blen, exp, flag_mask);
if (exp->e_uuid == NULL || different_fs) {
char u[16];
@@ -917,8 +909,13 @@ static int dump_to_cache(int f, char *buf, int buflen, char *domain,
qword_add(&bp, &blen, "uuid");
qword_addhex(&bp, &blen, u, 16);
}
- } else
+ xlog(D_AUTH, "granted access to %s for %s",
+ path, *domain == '$' ? domain+1 : domain);
+ } else {
qword_adduint(&bp, &blen, now + ttl);
+ xlog(D_AUTH, "denied access to %s for %s",
+ path, *domain == '$' ? domain+1 : domain);
+ }
qword_addeol(&bp, &blen);
if (blen <= 0) return -1;
if (write(f, buf, bp - buf) != bp - buf) return -1;
@@ -1421,6 +1418,40 @@ int cache_process_req(fd_set *readfds)
return cnt;
}
+/**
+ * cache_process_loop - process incoming upcalls
+ */
+void cache_process_loop(void)
+{
+ fd_set readfds;
+ int selret;
+
+ FD_ZERO(&readfds);
+
+ for (;;) {
+
+ 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;
+ xlog(L_ERROR, "my_svc_run() - select: %m");
+ return;
+
+ default:
+ cache_process_req(&readfds);
+ v4clients_process(&readfds);
+ }
+ }
+}
+
/*
* Give IP->domain and domain+path->options to kernel
diff --git a/support/export/export.h b/support/export/export.h
new file mode 100644
index 00000000..8d5a0d30
--- /dev/null
+++ b/support/export/export.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 Red Hat <nfs@redhat.com>
+ *
+ * support/export/export.h
+ *
+ * Declarations for export support
+ */
+
+#ifndef EXPORT_H
+#define EXPORT_H
+
+#include "nfslib.h"
+#include "exportfs.h"
+
+unsigned int auth_reload(void);
+nfs_export * auth_authenticate(const char *what,
+ const struct sockaddr *caller,
+ const char *path);
+
+void cache_open(void);
+void cache_set_fds(fd_set *fdset);
+int cache_process_req(fd_set *readfds);
+void cache_process_loop(void);
+
+void v4clients_init(void);
+void v4clients_set_fds(fd_set *fdset);
+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);
+
+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);
+
+static inline bool is_ipaddr_client(char *dom)
+{
+ return dom[0] == '$';
+}
+#endif /* EXPORT__H */
diff --git a/support/export/v4clients.c b/support/export/v4clients.c
new file mode 100644
index 00000000..dd985463
--- /dev/null
+++ b/support/export/v4clients.c
@@ -0,0 +1,227 @@
+/*
+ * support/export/v4clients.c
+ *
+ * Montior clients appearing in, and disappearing from, /proc/fs/nfsd/clients
+ * and log relevant information.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/inotify.h>
+#include <errno.h>
+#include "export.h"
+
+/* search.h declares 'struct entry' and nfs_prot.h
+ * does too. Easiest fix is to trick search.h into
+ * calling its struct "struct Entry".
+ */
+#define entry Entry
+#include <search.h>
+#undef entry
+
+static int clients_fd = -1;
+
+void v4clients_init(void)
+{
+ if (clients_fd >= 0)
+ return;
+ clients_fd = inotify_init1(IN_NONBLOCK);
+ if (clients_fd < 0) {
+ xlog_err("Unable to initialise v4clients watcher: %s\n",
+ strerror(errno));
+ return;
+ }
+ if (inotify_add_watch(clients_fd, "/proc/fs/nfsd/clients",
+ IN_CREATE | IN_DELETE) < 0) {
+ xlog_err("Unable to watch /proc/fs/nfsd/clients: %s\n",
+ strerror(errno));
+ close(clients_fd);
+ clients_fd = -1;
+ return;
+ }
+}
+
+void v4clients_set_fds(fd_set *fdset)
+{
+ if (clients_fd >= 0)
+ FD_SET(clients_fd, fdset);
+}
+
+static void *tree_root;
+static int have_unconfirmed;
+
+struct ent {
+ unsigned long num;
+ char *clientid;
+ char *addr;
+ int vers;
+ int unconfirmed;
+ int wid;
+};
+
+static int ent_cmp(const void *av, const void *bv)
+{
+ const struct ent *a = av;
+ const struct ent *b = bv;
+
+ if (a->num < b->num)
+ return -1;
+ if (a->num > b->num)
+ return 1;
+ return 0;
+}
+
+static void free_ent(struct ent *ent)
+{
+ free(ent->clientid);
+ free(ent->addr);
+ free(ent);
+}
+
+static char *dup_line(char *line)
+{
+ char *ret;
+ char *e = strchr(line, '\n');
+ if (!e)
+ e = line + strlen(line);
+ ret = malloc(e - line + 1);
+ if (ret) {
+ memcpy(ret, line, e - line);
+ ret[e-line] = 0;
+ }
+ return ret;
+}
+
+static void read_info(struct ent *key)
+{
+ char buf[2048];
+ char *path;
+ int was_unconfirmed = key->unconfirmed;
+ FILE *f;
+
+ if (asprintf(&path, "/proc/fs/nfsd/clients/%lu/info", key->num) < 0)
+ return;
+
+ f = fopen(path, "r");
+ if (!f) {
+ free(path);
+ return;
+ }
+ if (key->wid < 0)
+ key->wid = inotify_add_watch(clients_fd, path, IN_MODIFY);
+
+ while (fgets(buf, sizeof(buf), f)) {
+ if (strncmp(buf, "clientid: ", 10) == 0) {
+ free(key->clientid);
+ key->clientid = dup_line(buf+10);
+ }
+ if (strncmp(buf, "address: ", 9) == 0) {
+ free(key->addr);
+ key->addr = dup_line(buf+9);
+ }
+ if (strncmp(buf, "minor version: ", 15) == 0)
+ key->vers = atoi(buf+15);
+ if (strncmp(buf, "status: ", 8) == 0 &&
+ strstr(buf, " unconfirmed") != NULL) {
+ key->unconfirmed = 1;
+ have_unconfirmed = 1;
+ }
+ if (strncmp(buf, "status: ", 8) == 0 &&
+ strstr(buf, " confirmed") != NULL)
+ key->unconfirmed = 0;
+ }
+ fclose(f);
+ free(path);
+
+ if (was_unconfirmed && !key->unconfirmed)
+ xlog(L_NOTICE, "v4.%d client attached: %s from %s",
+ key->vers, key->clientid ?: "-none-",
+ key->addr ?: "-none-");
+ if (!key->unconfirmed && key->wid >= 0) {
+ inotify_rm_watch(clients_fd, key->wid);
+ key->wid = -1;
+ }
+}
+
+static void add_id(int id)
+{
+ struct ent **ent;
+ struct ent *key;
+
+ key = calloc(1, sizeof(*key));
+ if (!key) {
+ return;
+ }
+ key->num = id;
+ key->wid = -1;
+
+ ent = tsearch(key, &tree_root, ent_cmp);
+
+ if (!ent || *ent != key)
+ /* Already existed, or insertion failed */
+ free_ent(key);
+ else
+ read_info(key);
+}
+
+static void del_id(unsigned long id)
+{
+ struct ent key = {.num = id};
+ struct ent **e, *ent;
+
+ e = tfind(&key, &tree_root, ent_cmp);
+ if (!e || !*e)
+ return;
+ ent = *e;
+ tdelete(ent, &tree_root, ent_cmp);
+ if (!ent->unconfirmed)
+ xlog(L_NOTICE, "v4.%d client detached: %s from %s",
+ ent->vers, ent->clientid, ent->addr);
+ if (ent->wid >= 0)
+ inotify_rm_watch(clients_fd, ent->wid);
+ free_ent(ent);
+}
+
+static void check_id(unsigned long id)
+{
+ struct ent key = {.num = id};
+ struct ent **e, *ent;
+
+ e = tfind(&key, &tree_root, ent_cmp);
+ if (!e || !*e)
+ return;
+ ent = *e;
+ if (ent->unconfirmed)
+ read_info(ent);
+}
+
+int v4clients_process(fd_set *fdset)
+{
+ char buf[4096] __attribute__((aligned(__alignof__(struct inotify_event))));
+ const struct inotify_event *ev;
+ ssize_t len;
+ char *ptr;
+
+ if (clients_fd < 0 ||
+ !FD_ISSET(clients_fd, fdset))
+ return 0;
+
+ while ((len = read(clients_fd, buf, sizeof(buf))) > 0) {
+ for (ptr = buf; ptr < buf + len;
+ ptr += sizeof(struct inotify_event) + ev->len) {
+ int id;
+ ev = (const struct inotify_event *)ptr;
+
+ id = atoi(ev->name);
+ if (id <= 0)
+ continue;
+ if (ev->mask & IN_CREATE)
+ add_id(id);
+ if (ev->mask & IN_DELETE)
+ del_id(id);
+ if (ev->mask & IN_MODIFY)
+ check_id(id);
+ }
+ }
+ return 1;
+}
diff --git a/utils/mountd/v4root.c b/support/export/v4root.c
similarity index 99%
rename from utils/mountd/v4root.c
rename to support/export/v4root.c
index 8ec33fb0..4d33117f 100644
--- a/utils/mountd/v4root.c
+++ b/support/export/v4root.c
@@ -47,7 +47,7 @@ static nfs_export pseudo_root = {
.e_nsqgids = 0,
.e_fsid = 0,
.e_mountpoint = NULL,
- .e_ttl = DEFAULT_TTL,
+ .e_ttl = 0,
},
.m_exported = 0,
.m_xtabent = 1,
@@ -86,6 +86,7 @@ v4root_create(char *path, nfs_export *export)
struct exportent *curexp = &export->m_export;
dupexportent(&eep, &pseudo_root.m_export);
+ eep.e_ttl = default_ttl;
eep.e_hostname = curexp->e_hostname;
strncpy(eep.e_path, path, sizeof(eep.e_path)-1);
if (strcmp(path, "/") != 0)
diff --git a/support/include/exportfs.h b/support/include/exportfs.h
index 4e0d9d13..bfae1957 100644
--- a/support/include/exportfs.h
+++ b/support/include/exportfs.h
@@ -105,7 +105,8 @@ typedef struct mexport {
} nfs_export;
#define HASH_TABLE_SIZE 1021
-#define DEFAULT_TTL (30 * 60)
+
+extern int default_ttl;
typedef struct _exp_hash_entry {
nfs_export * p_first;
diff --git a/support/nfs/exports.c b/support/nfs/exports.c
index a7582cae..4dd2e5d3 100644
--- a/support/nfs/exports.c
+++ b/support/nfs/exports.c
@@ -47,6 +47,8 @@ struct flav_info flav_map[] = {
const int flav_map_size = sizeof(flav_map)/sizeof(flav_map[0]);
+int default_ttl = 30 * 60;
+
static char *efname = NULL;
static XFILE *efp = NULL;
static int first;
@@ -100,7 +102,7 @@ static void init_exportent (struct exportent *ee, int fromkernel)
ee->e_nsquids = 0;
ee->e_nsqgids = 0;
ee->e_uuid = NULL;
- ee->e_ttl = DEFAULT_TTL;
+ ee->e_ttl = default_ttl;
}
struct exportent *
diff --git a/systemd/nfs.conf.man b/systemd/nfs.conf.man
index 498d93a9..aa4630bb 100644
--- a/systemd/nfs.conf.man
+++ b/systemd/nfs.conf.man
@@ -157,6 +157,8 @@ Recognized values:
.BR port ,
.BR threads ,
.BR reverse-lookup ,
+.BR cache-use-upaddr ,
+.BR ttl ,
.BR state-directory-path ,
.BR ha-callout .
@@ -166,6 +168,14 @@ section, are used to configure mountd. See
.BR rpc.mountd (8)
for details.
+Note that setting
+.B "\[dq]debug = auth\[dq]"
+for
+.B mountd
+is equivalent to providing the
+.B \-\-log\-auth
+option.
+
The
.B state-directory-path
value in the
diff --git a/utils/mountd/Makefile.am b/utils/mountd/Makefile.am
index 73eeb3f3..c41f06de 100644
--- a/utils/mountd/Makefile.am
+++ b/utils/mountd/Makefile.am
@@ -13,8 +13,8 @@ KPREFIX = @kprefix@
sbin_PROGRAMS = mountd
noinst_HEADERS = fsloc.h
-mountd_SOURCES = mountd.c mount_dispatch.c auth.c rmtab.c cache.c \
- svc_run.c fsloc.c v4root.c mountd.h
+mountd_SOURCES = mountd.c mount_dispatch.c rmtab.c \
+ svc_run.c fsloc.c mountd.h
mountd_LDADD = ../../support/export/libexport.a \
../../support/nfs/libnfs.la \
../../support/misc/libmisc.a \
diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
index 0b891121..2b342377 100644
--- a/utils/mountd/mountd.c
+++ b/utils/mountd/mountd.c
@@ -30,6 +30,7 @@
#include "rpcmisc.h"
#include "pseudoflavors.h"
#include "nfslib.h"
+#include "export.h"
extern void my_svc_run(void);
@@ -73,8 +74,12 @@ static struct option longopts[] =
{ "reverse-lookup", 0, 0, 'r' },
{ "manage-gids", 0, 0, 'g' },
{ "no-udp", 0, 0, 'u' },
+ { "log-auth", 0, 0, 'l'},
+ { "cache-use-ipaddr", 0, 0, 'i'},
+ { "ttl", 1, 0, 'T'},
{ NULL, 0, 0, 0 }
};
+static char shortopts[] = "o:nFd:p:P:hH:N:V:vurs:t:gliT:";
#define NFSVERSBIT(vers) (0x1 << (vers - 1))
#define NFSVERSBIT_ALL (NFSVERSBIT(2) | NFSVERSBIT(3) | NFSVERSBIT(4))
@@ -669,6 +674,7 @@ main(int argc, char **argv)
int port = 0;
int descriptors = 0;
int c;
+ int ttl;
int vers;
struct sigaction sa;
struct rlimit rlim;
@@ -687,6 +693,8 @@ main(int argc, char **argv)
num_threads = conf_get_num("mountd", "threads", num_threads);
reverse_resolve = conf_get_bool("mountd", "reverse-lookup", reverse_resolve);
ha_callout_prog = conf_get_str("mountd", "ha-callout");
+ if (conf_get_bool("mountd", "cache-use-ipaddr", 0))
+ use_ipaddr = 2;
s = conf_get_str("mountd", "state-directory-path");
if (s && !state_setup_basedir(argv[0], s))
@@ -710,10 +718,13 @@ main(int argc, char **argv)
NFSCTL_VERUNSET(nfs_version, vers);
}
+ ttl = conf_get_num("mountd", "ttl", default_ttl);
+ if (ttl > 0)
+ default_ttl = ttl;
/* Parse the command line options and arguments. */
opterr = 0;
- while ((c = getopt_long(argc, argv, "o:nFd:p:P:hH:N:V:vurs:t:g", longopts, NULL)) != EOF)
+ while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != EOF)
switch (c) {
case 'g':
manage_gids = 1;
@@ -784,6 +795,21 @@ main(int argc, char **argv)
case 'u':
NFSCTL_UDPUNSET(_rpcprotobits);
break;
+ case 'l':
+ xlog_sconfig("auth", 1);
+ break;
+ case 'i':
+ use_ipaddr = 2;
+ break;
+ case 'T':
+ ttl = atoi(optarg);
+ if (ttl <= 0) {
+ fprintf(stderr, "%s: bad ttl number of seconds: %s\n",
+ argv[0], optarg);
+ usage(argv[0], 1);
+ }
+ default_ttl = ttl;
+ break;
case 0:
break;
case '?':
@@ -888,6 +914,8 @@ main(int argc, char **argv)
if (num_threads > 1)
fork_workers();
+ v4clients_init();
+
xlog(L_NOTICE, "Version " VERSION " starting");
my_svc_run();
@@ -903,6 +931,7 @@ usage(const char *prog, int n)
{
fprintf(stderr,
"Usage: %s [-F|--foreground] [-h|--help] [-v|--version] [-d kind|--debug kind]\n"
+" [-l|--log-auth] [-i|--cache-use-ipaddr] [-T|--ttl ttl]\n"
" [-o num|--descriptors num]\n"
" [-p|--port port] [-V version|--nfs-version version]\n"
" [-N version|--no-nfs-version version] [-n|--no-tcp]\n"
diff --git a/utils/mountd/mountd.h b/utils/mountd/mountd.h
index f058f01d..d3077531 100644
--- a/utils/mountd/mountd.h
+++ b/utils/mountd/mountd.h
@@ -60,9 +60,4 @@ 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);
-static inline bool is_ipaddr_client(char *dom)
-{
- return dom[0] == '$';
-}
-
#endif /* MOUNTD_H */
diff --git a/utils/mountd/mountd.man b/utils/mountd/mountd.man
index 8a7943f8..2a91e193 100644
--- a/utils/mountd/mountd.man
+++ b/utils/mountd/mountd.man
@@ -13,24 +13,24 @@ The
.B rpc.mountd
daemon implements the server side of the NFS MOUNT protocol,
an NFS side protocol used by NFS version 2 [RFC1094] and NFS version 3 [RFC1813].
+It also responds to requests from the Linux kernel to authenticate
+clients and provides details of access permissions.
.PP
-An NFS server maintains a table of local physical file systems
-that are accessible to NFS clients.
-Each file system in this table is referred to as an
-.IR "exported file system" ,
-or
-.IR export ,
-for short.
-.PP
-Each file system in the export table has an access control list.
-.B rpc.mountd
-uses these access control lists to determine
-whether an NFS client is permitted to access a given file system.
-For details on how to manage your NFS server's export table, see the
-.BR exports (5)
-and
-.BR exportfs (8)
-man pages.
+The NFS server
+.RI ( nfsd )
+maintains a cache of authentication and authorization information which
+is used to identify the source of each request, and then what access
+permissions that source has to any local filesystem. When required
+information is not found in the cache, the server sends a request to
+.B mountd
+to fill in the missing information. Mountd uses a table of information
+stored in
+.B /var/lib/nfs/etab
+and maintained by
+.BR exportfs (8),
+possibly based on the contents of
+.BR exports (5),
+to respond to each request.
.SS Mounting exported NFS File Systems
The NFS MOUNT protocol has several procedures.
The most important of these are
@@ -78,11 +78,69 @@ A client may continue accessing an export even after invoking UMNT.
If the client reboots without sending a UMNT request, stale entries
remain for that client in
.IR /var/lib/nfs/rmtab .
+.SS Mounting File Systems with NFSv4
+Version 4 (and later) of NFS does not use a separate NFS MOUNT
+protocol. Instead mounting is performed using regular NFS requests
+handled by the NFS server in the Linux kernel
+.RI ( nfsd ).
+Consequently
+.I /var/lib/nfs/rmtab
+is not updated to reflect any NFSv4 activity.
.SH OPTIONS
.TP
.B \-d kind " or " \-\-debug kind
Turn on debugging. Valid kinds are: all, auth, call, general and parse.
.TP
+.BR \-l " or " \-\-log\-auth
+Enable logging of responses to authentication and access requests from
+nfsd. Each response is then cached by the kernel for 30 minutes (or as set by
+.B \-\-ttl
+below), and will be refreshed after 15 minutes (half the ttl time) if
+the relevant client remains active.
+Note that
+.B -l
+is equivalent to
+.B "-d auth"
+and so can be enabled in
+.B /etc/nfs.conf
+with
+.B "\[dq]debug = auth\[dq]"
+in the
+.B "[mountd]"
+section.
+.IP
+.B rpc.mountd
+will always log authentication responses to MOUNT requests when NFSv3 is
+used, but to get similar logs for NFSv4, this option is required.
+.TP
+.BR \-i " or " \-\-cache\-use\-ipaddr
+Normally each client IP address is matched against each host identifier
+(name, wildcard, netgroup etc) found in
+.B /etc/exports
+and a combined identity is formed from all matching identifiers.
+Often many clients will map to the same combined identity so performing
+this mapping reduces the number of distinct access details that the
+kernel needs to store.
+Specifying the
+.B \-i
+option suppresses this mapping so that access to each filesystem is
+requested and cached separately for each client IP address. Doing this
+can increase the burden of updating the cache slightly, but can make the
+log messages produced by the
+.B -l
+option easier to read.
+.TP
+.B \-T " or " \-\-ttl
+Provide a time-to-live (TTL) for cached information given to the kernel.
+The kernel will normally request an update if the information is needed
+after half of this time has expired. Increasing the provided number,
+which is in seconds, reduces the rate of cache update requests, and this
+is particularly noticeable when these requests are logged with
+.BR \-l .
+However increasing also means that changes to hostname to address
+mappings can take longer to be noticed.
+The default TTL is 1800 (30 minutes).
+.TP
.B \-F " or " \-\-foreground
Run in foreground (do not daemonize)
.TP
@@ -213,9 +271,11 @@ Values recognized in the
.B [mountd]
section include
.BR manage-gids ,
+.BR cache\-use\-ipaddr ,
.BR descriptors ,
.BR port ,
.BR threads ,
+.BR ttl ,
.BR reverse-lookup ", and"
.BR state-directory-path ,
.B ha-callout
@@ -265,5 +325,9 @@ table of clients accessing server's exports
RFC 1094 - "NFS: Network File System Protocol Specification"
.br
RFC 1813 - "NFS Version 3 Protocol Specification"
+.br
+RFC 7530 - "Network File System (NFS) Version 4 Protocol"
+.br
+RFC 8881 - "Network File System (NFS) Version 4 Minor Version 1 Protocol"
.SH AUTHOR
Olaf Kirch, H. J. Lu, G. Allan Morris III, and a host of others.
diff --git a/utils/mountd/svc_run.c b/utils/mountd/svc_run.c
index 41b96d7f..167b9757 100644
--- a/utils/mountd/svc_run.c
+++ b/utils/mountd/svc_run.c
@@ -56,10 +56,9 @@
#ifdef HAVE_LIBTIRPC
#include <rpc/rpc_com.h>
#endif
+#include "export.h"
void my_svc_run(void);
-void cache_set_fds(fd_set *fdset);
-int cache_process_req(fd_set *readfds);
#if defined(__GLIBC__) && LONG_MAX != INT_MAX
/* bug in glibc 2.3.6 and earlier, we need
@@ -101,6 +100,7 @@ my_svc_run(void)
readfds = svc_fdset;
cache_set_fds(&readfds);
+ v4clients_set_fds(&readfds);
selret = select(FD_SETSIZE, &readfds,
(void *) 0, (void *) 0, (struct timeval *) 0);
@@ -116,6 +116,7 @@ my_svc_run(void)
default:
selret -= cache_process_req(&readfds);
+ selret -= v4clients_process(&readfds);
if (selret)
svc_getreqset(&readfds);
}

View File

@ -0,0 +1,24 @@
diff -up nfs-utils-2.3.3/support/export/v4clients.c.orig nfs-utils-2.3.3/support/export/v4clients.c
--- nfs-utils-2.3.3/support/export/v4clients.c.orig 2022-09-26 11:36:22.803929066 -0400
+++ nfs-utils-2.3.3/support/export/v4clients.c 2022-09-26 11:38:38.221187835 -0400
@@ -8,6 +8,7 @@
#include <unistd.h>
#include <stdlib.h>
#include <sys/inotify.h>
+#include <sys/stat.h>
#include <errno.h>
#include "export.h"
@@ -23,6 +24,12 @@ static int clients_fd = -1;
void v4clients_init(void)
{
+ struct stat sb;
+
+ if (!stat("/proc/fs/nfsd/clients", &sb) == 0 ||
+ !S_ISDIR(sb.st_mode))
+ return;
+
if (clients_fd >= 0)
return;
clients_fd = inotify_init1(IN_NONBLOCK);

View File

@ -0,0 +1,29 @@
commit 3e81185037cf97990e4598218f56d92dd70d6269
Author: NeilBrown <neilb@suse.de>
Date: Tue Oct 20 13:19:10 2020 -0400
clddb-tool was recently renamed to nfsdclddb.
Unfortunately the nfsdcld man page wasn't told.
Signed-off-by: NeilBrown <neilb@suse.de>
Signed-off-by: Steve Dickson <steved@redhat.com>
diff --git a/utils/nfsdcld/nfsdcld.man b/utils/nfsdcld/nfsdcld.man
index 4c2b1e80..861f1c49 100644
--- a/utils/nfsdcld/nfsdcld.man
+++ b/utils/nfsdcld/nfsdcld.man
@@ -209,12 +209,12 @@ not necessary after upgrading \fBnfsdcld\fR, however \fBnfsd\fR will not use a l
version until restart. A restart of \fBnfsd is necessary\fR after downgrading \fBnfsdcld\fR,
to ensure that \fBnfsd\fR does not use an upcall version that \fBnfsdcld\fR does not support.
Additionally, a downgrade of \fBnfsdcld\fR requires the schema of the on-disk database to
-be downgraded as well. That can be accomplished using the \fBclddb-tool\fR(8) utility.
+be downgraded as well. That can be accomplished using the \fBnfsdclddb\fR(8) utility.
.SH FILES
.TP
.B /var/lib/nfs/nfsdcld/main.sqlite
.SH SEE ALSO
-.BR nfsdcltrack "(8), " clddb-tool (8)
+.BR nfsdcltrack "(8), " nfsdclddb (8)
.SH "AUTHORS"
.IX Header "AUTHORS"
The nfsdcld daemon was developed by Jeff Layton <jlayton@redhat.com>

View File

@ -0,0 +1,27 @@
commit 0095435db8228d5a88ec35a63cb64271e2e648a8
Author: Steve Dickson <steved@redhat.com>
Date: Thu Dec 19 12:48:31 2019 -0500
libnfsidmap: Turn off default verbosity
Commit f080188e changed the library's verbosity
to be on by default. The patch turns it off by
default
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1774787
Signed-off-by: Steve Dickson <steved@redhat.com>
diff --git a/support/nfsidmap/libnfsidmap.c b/support/nfsidmap/libnfsidmap.c
index 9299e652..d11710f1 100644
--- a/support/nfsidmap/libnfsidmap.c
+++ b/support/nfsidmap/libnfsidmap.c
@@ -101,7 +101,7 @@ static void default_logger(const char *fmt, ...)
#pragma GCC visibility pop
nfs4_idmap_log_function_t idmap_log_func = default_logger;
-int idmap_verbosity = 2;
+int idmap_verbosity = 0;
#pragma GCC visibility push(hidden)
static int id_as_chars(char *name, uid_t *id)

View File

@ -0,0 +1,12 @@
diff -up nfs-utils-2.3.3/tools/nfs-iostat/nfs-iostat.py.orig nfs-utils-2.3.3/tools/nfs-iostat/nfs-iostat.py
--- nfs-utils-2.3.3/tools/nfs-iostat/nfs-iostat.py.orig 2020-12-10 10:38:26.462195326 -0500
+++ nfs-utils-2.3.3/tools/nfs-iostat/nfs-iostat.py 2020-12-10 10:45:47.210671473 -0500
@@ -380,6 +380,8 @@ class DeviceData:
sends = float(self.__rpc_data['rpcsends'])
if sample_time == 0:
sample_time = float(self.__nfs_data['age'])
+ if sample_time == 0:
+ sample_time = 1;
return (sends / sample_time)
def display_iostats(self, sample_time, which):

View File

@ -0,0 +1,37 @@
diff -up nfs-utils-2.3.3/tools/mountstats/mountstats.py.orig nfs-utils-2.3.3/tools/mountstats/mountstats.py
--- nfs-utils-2.3.3/tools/mountstats/mountstats.py.orig 2020-12-10 10:48:17.319579958 -0500
+++ nfs-utils-2.3.3/tools/mountstats/mountstats.py 2020-12-10 10:52:42.481484160 -0500
@@ -943,10 +943,11 @@ def print_iostat_summary(old, new, devic
if not old or device not in old:
stats.display_iostats(time)
else:
- old_stats = DeviceData()
- old_stats.parse_stats(old[device])
- diff_stats = stats.compare_iostats(old_stats)
- diff_stats.display_iostats(time)
+ if ("fstype autofs" not in str(old[device])) and ("fstype autofs" not in str(new[device])):
+ old_stats = DeviceData()
+ old_stats.parse_stats(old[device])
+ diff_stats = stats.compare_iostats(old_stats)
+ diff_stats.display_iostats(time)
def iostat_command(args):
"""iostat-like command for NFS mount points
diff -up nfs-utils-2.3.3/tools/nfs-iostat/nfs-iostat.py.orig nfs-utils-2.3.3/tools/nfs-iostat/nfs-iostat.py
--- nfs-utils-2.3.3/tools/nfs-iostat/nfs-iostat.py.orig 2020-12-10 10:48:17.316579880 -0500
+++ nfs-utils-2.3.3/tools/nfs-iostat/nfs-iostat.py 2020-12-10 10:52:42.481484160 -0500
@@ -467,10 +467,13 @@ def parse_stats_file(filename):
def print_iostat_summary(old, new, devices, time, options):
stats = {}
diff_stats = {}
+ devicelist = []
if old:
# Trim device list to only include intersection of old and new data,
# this addresses umounts due to autofs mountpoints
- devicelist = [x for x in old if x in devices]
+ for device in devices:
+ if "fstype autofs" not in str(old[device]):
+ devicelist.append(device)
else:
devicelist = devices

View File

@ -0,0 +1,53 @@
commit b5381c96298d75ba66625a007e2390e2b501850d
Author: Trond Myklebust <trond.myklebust@hammerspace.com>
Date: Wed Jan 29 10:45:39 2020 -0500
manpage: Add a description of the 'softreval' / 'nosoftreval' mount option
Add a description of the 'softreval' / 'nosoftreval' mount options on
the 'nfs' generic manpage.
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man
index 84462cd7..6f79c63a 100644
--- a/utils/mount/nfs.man
+++ b/utils/mount/nfs.man
@@ -121,6 +121,36 @@ option may mitigate some of the risks of using the
.B soft
option.
.TP 1.5i
+.BR softreval " / " nosoftreval
+In cases where the NFS server is down, it may be useful to
+allow the NFS client to continue to serve up paths and
+attributes from cache after
+.B retrans
+attempts to revalidate that cache have timed out.
+This may, for instance, be helpful when trying to unmount a
+filesystem tree from a server that is permanently down.
+.IP
+It is possible to combine
+.BR softreval
+with the
+.B soft
+mount option, in which case operations that cannot be served up
+from cache will time out and return an error after
+.B retrans
+attempts. The combination with the default
+.B hard
+mount option implies those uncached operations will continue to
+retry until a response is received from the server.
+.IP
+Note: the default mount option is
+.BR nosoftreval
+which disallows fallback to cache when revalidation fails, and
+instead follows the behavior dictated by the
+.B hard
+or
+.B soft
+mount option.
+.TP 1.5i
.BR intr " / " nointr
This option is provided for backward compatibility.
It is ignored after kernel 2.6.25.

View File

@ -0,0 +1,399 @@
diff --git a/.gitignore b/.gitignore
index e97b31f5..e504d492 100644
--- a/.gitignore
+++ b/.gitignore
@@ -60,6 +60,8 @@ utils/statd/statd
tools/locktest/testlk
tools/getiversion/getiversion
tools/nfsconf/nfsconf
+tools/nfsrahead/nfsrahead
+tools/nfsrahead/99-nfs_bdi.rules
support/export/mount.h
support/export/mount_clnt.c
support/export/mount_xdr.c
diff --git a/configure.ac b/configure.ac
index 6d464ac5..f462a645 100644
--- a/configure.ac
+++ b/configure.ac
@@ -639,6 +639,7 @@ AC_CONFIG_FILES([
tools/rpcgen/Makefile
tools/mountstats/Makefile
tools/nfs-iostat/Makefile
+ tools/nfsrahead/Makefile
tools/rpcctl/Makefile
tools/nfsdclnts/Makefile
tools/nfsconf/Makefile
diff --git a/nfs.conf b/nfs.conf
index 86ed7d53..30f9e109 100644
--- a/nfs.conf
+++ b/nfs.conf
@@ -5,6 +5,10 @@
[general]
# pipefs-directory=/var/lib/nfs/rpc_pipefs
#
+[nfsrahead]
+# nfs=15000
+# nfs4=16000
+#
[exportfs]
# debug=0
#
diff --git a/systemd/nfs.conf.man b/systemd/nfs.conf.man
index f32c690b..ebbf28d0 100644
--- a/systemd/nfs.conf.man
+++ b/systemd/nfs.conf.man
@@ -245,6 +245,17 @@ Only
.B debug=
is recognized.
+.TP
+.B nfsrahead
+Recognized values:
+.BR nfs ,
+.BR nfsv4 ,
+.BR default .
+
+See
+.BR nfsrahead (5)
+for deatils.
+
.SH FILES
.I /etc/nfs.conf
.SH SEE ALSO
diff --git a/tools/Makefile.am b/tools/Makefile.am
index c3feabbe..40c17c37 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -12,6 +12,6 @@ if CONFIG_NFSDCLD
OPTDIRS += nfsdclddb
endif
-SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat rpcctl nfsdclnts $(OPTDIRS)
+SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat rpcctl nfsdclnts nfsrahead $(OPTDIRS)
MAINTAINERCLEANFILES = Makefile.in
diff --git a/tools/nfsrahead/99-nfs.rules b/tools/nfsrahead/99-nfs.rules
new file mode 100644
index 00000000..c74914b2
--- /dev/null
+++ b/tools/nfsrahead/99-nfs.rules
@@ -0,0 +1 @@
+SUBSYSTEM=="bdi", ACTION=="add", PROGRAM="/usr/libexec/nfsrahead %k", ATTR{read_ahead_kb}="%c"
diff --git a/tools/nfsrahead/99-nfs.rules.in b/tools/nfsrahead/99-nfs.rules.in
new file mode 100644
index 00000000..648813c5
--- /dev/null
+++ b/tools/nfsrahead/99-nfs.rules.in
@@ -0,0 +1 @@
+SUBSYSTEM=="bdi", ACTION=="add", PROGRAM="_libexecdir_/nfsrahead %k", ATTR{read_ahead_kb}="%c"
diff --git a/tools/nfsrahead/Makefile.am b/tools/nfsrahead/Makefile.am
new file mode 100644
index 00000000..845ea0d5
--- /dev/null
+++ b/tools/nfsrahead/Makefile.am
@@ -0,0 +1,16 @@
+libexec_PROGRAMS = nfsrahead
+nfsrahead_SOURCES = main.c
+nfsrahead_LDFLAGS= -lmount
+nfsrahead_LDADD = ../../support/nfs/libnfsconf.la
+
+man5_MANS = nfsrahead.man
+EXTRA_DIST = $(man5_MANS)
+
+udev_rulesdir = /usr/lib/udev/rules.d/
+udev_rules_DATA = 99-nfs.rules
+
+99-nfs.rules: 99-nfs.rules.in $(builddefs)
+ $(SED) "s|_libexecdir_|@libexecdir@|g" 99-nfs.rules.in > $@
+
+clean-local:
+ $(RM) 99-nfs.rules
diff --git a/tools/nfsrahead/main.c b/tools/nfsrahead/main.c
new file mode 100644
index 00000000..c83c6f71
--- /dev/null
+++ b/tools/nfsrahead/main.c
@@ -0,0 +1,192 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <libmount/libmount.h>
+#include <sys/sysmacros.h>
+
+#include "xlog.h"
+#include "conffile.h"
+
+#ifndef MOUNTINFO_PATH
+#define MOUNTINFO_PATH "/proc/self/mountinfo"
+#endif
+
+#define CONF_NAME "nfsrahead"
+#define NFS_DEFAULT_READAHEAD 128
+
+/* Device information from the system */
+struct device_info {
+ char *device_number;
+ dev_t dev;
+ char *mountpoint;
+ char *fstype;
+};
+
+/* Convert a string in the format n:m to a device number */
+static int fill_device_number(struct device_info *info)
+{
+ char *s = strdup(info->device_number), *p;
+ char *maj_s, *min_s;
+ unsigned int maj, min;
+ int err = -EINVAL;
+
+ maj_s = p = s;
+ for ( ; *p != ':' && *p != '\0'; p++)
+ ;
+
+ if (*p == '\0')
+ goto out_free;
+
+ err = 0;
+ *p = '\0';
+ min_s = p + 1;
+
+ maj = strtol(maj_s, NULL, 10);
+ min = strtol(min_s, NULL, 10);
+
+ info->dev = makedev(maj, min);
+out_free:
+ free(s);
+ return err;
+}
+
+#define sfree(ptr) if (ptr) free(ptr)
+
+/* device_info maintenance */
+static void init_device_info(struct device_info *di, const char *device_number)
+{
+ di->device_number = strdup(device_number);
+ di->dev = 0;
+ di->mountpoint = NULL;
+ di->fstype = NULL;
+}
+
+
+static void free_device_info(struct device_info *di)
+{
+ sfree(di->mountpoint);
+ sfree(di->fstype);
+ sfree(di->device_number);
+}
+
+static int get_mountinfo(const char *device_number, struct device_info *device_info, const char *mountinfo_path)
+{
+ int ret = 0;
+ struct libmnt_table *mnttbl;
+ struct libmnt_fs *fs;
+ char *target;
+
+ init_device_info(device_info, device_number);
+ if ((ret = fill_device_number(device_info)) < 0)
+ goto out_free_device_info;
+
+ mnttbl = mnt_new_table();
+
+ if ((ret = mnt_table_parse_file(mnttbl, mountinfo_path)) < 0) {
+ xlog(D_GENERAL, "Failed to parse %s\n", mountinfo_path);
+ goto out_free_tbl;
+ }
+
+ if ((fs = mnt_table_find_devno(mnttbl, device_info->dev, MNT_ITER_FORWARD)) == NULL) {
+ ret = ENOENT;
+ goto out_free_tbl;
+ }
+
+ if ((target = (char *)mnt_fs_get_target(fs)) == NULL) {
+ ret = ENOENT;
+ goto out_free_fs;
+ }
+
+ device_info->mountpoint = strdup(target);
+ target = (char *)mnt_fs_get_fstype(fs);
+ if (target)
+ device_info->fstype = strdup(target);
+
+out_free_fs:
+ mnt_free_fs(fs);
+out_free_tbl:
+ mnt_free_table(mnttbl);
+out_free_device_info:
+ free(device_info->device_number);
+ device_info->device_number = NULL;
+ return ret;
+}
+
+static int get_device_info(const char *device_number, struct device_info *device_info)
+{
+ int ret = ENOENT;
+ for (int retry_count = 0; retry_count < 10 && ret != 0; retry_count++)
+ ret = get_mountinfo(device_number, device_info, MOUNTINFO_PATH);
+
+ return ret;
+}
+
+static int conf_get_readahead(const char *kind) {
+ int readahead = 0;
+
+ if((readahead = conf_get_num(CONF_NAME, kind, -1)) == -1)
+ readahead = conf_get_num(CONF_NAME, "default", NFS_DEFAULT_READAHEAD);
+
+ return readahead;
+}
+
+int main(int argc, char **argv)
+{
+ int ret = 0, retry, opt;
+ struct device_info device;
+ unsigned int readahead = 128, log_level, log_stderr = 0;
+
+
+ log_level = D_ALL & ~D_GENERAL;
+ while((opt = getopt(argc, argv, "dF")) != -1) {
+ switch (opt) {
+ case 'd':
+ log_level = D_ALL;
+ break;
+ case 'F':
+ log_stderr = 1;
+ break;
+ }
+ }
+
+ conf_init_file(NFS_CONFFILE);
+
+ xlog_stderr(log_stderr);
+ xlog_syslog(~log_stderr);
+ xlog_config(log_level, 1);
+ xlog_open(CONF_NAME);
+
+ // xlog_err causes the system to exit
+ if ((argc - optind) != 1)
+ xlog_err("expected the device number of a BDI; is udev ok?");
+
+ for (retry = 0; retry <= 10; retry++ )
+ if ((ret = get_device_info(argv[optind], &device)) == 0)
+ break;
+
+ if (ret != 0) {
+ xlog(D_GENERAL, "unable to find device %s\n", argv[optind]);
+ goto out;
+ }
+
+ if (strncmp("nfs", device.fstype, 3) != 0) {
+ xlog(D_GENERAL,
+ "not setting readahead for non supported fstype %s on device %s\n",
+ device.fstype, argv[optind]);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ readahead = conf_get_readahead(device.fstype);
+
+ xlog(D_FAC7, "setting %s readahead to %d\n", device.mountpoint, readahead);
+
+ printf("%d\n", readahead);
+
+out:
+ free_device_info(&device);
+ return ret;
+}
diff --git a/tools/nfsrahead/nfsrahead.man b/tools/nfsrahead/nfsrahead.man
new file mode 100644
index 00000000..5488f633
--- /dev/null
+++ b/tools/nfsrahead/nfsrahead.man
@@ -0,0 +1,72 @@
+.\" Manpage for nfsrahead.
+.nh
+.ad l
+.TH man 5 "08 Mar 2022" "1.0" "nfsrahead man page"
+.SH NAME
+
+nfsrahead \- Configure the readahead for NFS mounts
+
+.SH SYNOPSIS
+
+nfsrahead [-F] [-d] <device>
+
+.SH DESCRIPTION
+
+\fInfsrahead\fR is a tool intended to be used with udev to set the \fIread_ahead_kb\fR parameter of NFS mounts, according to the configuration file (see \fICONFIGURATION\fR). \fIdevice\fR is the device number for the NFS backing device as provided by the kernel.
+
+.SH OPTIONS
+.TP
+.B -F
+Send messages to
+.I stderr
+instead of
+.I syslog
+
+.TP
+.B -d
+Increase the debugging level.
+
+.SH CONFIGURATION
+.I nfsrahead
+is configured in
+.IR /etc/nfs.conf ,
+in the section titled
+.IR nfsrahead .
+It accepts the following configurations.
+
+.TP
+.B nfs=<value>
+The readahead value applied to NFSv3 mounts.
+
+.TP
+.B nfs4=<value>
+The readahead value applied to NFSv4 mounts.
+
+.TP
+.B default=<value>
+The default configuration when none of the configurations above is set.
+
+.SH EXAMPLE CONFIGURATION
+[nfsrahead]
+.br
+nfs=15000 # readahead of 15000 for NFSv3 mounts
+.br
+nfs4=16000 # readahead of 16000 for NFSv4 mounts
+.br
+default=128 # default is 128
+
+.SH SEE ALSO
+
+.BR mount.nfs (8),
+.BR nfs (5),
+.BR nfs.conf (5),
+.BR udev (7),
+.BR bcc-readahead (8)
+
+.SH BUGS
+
+No known bugs.
+
+.SH AUTHOR
+
+Thiago Rafael Becker <trbecker@gmail.com>
diff --git a/utils/nfsidmap/nfsidmap.man b/utils/nfsidmap/nfsidmap.man
index 2af16f31..1911c41b 100644
--- a/utils/nfsidmap/nfsidmap.man
+++ b/utils/nfsidmap/nfsidmap.man
@@ -2,7 +2,7 @@
.\"@(#)nfsidmap(8) - The NFS idmapper upcall program
.\"
.\" Copyright (C) 2010 Bryan Schumaker <bjschuma@netapp.com>
-.TH nfsidmap 5 "1 October 2010"
+.TH nfsidmap 8 "1 October 2010"
.SH NAME
nfsidmap \- The NFS idmapper upcall program
.SH SYNOPSIS

View File

@ -0,0 +1,12 @@
diff -up nfs-utils-2.3.3/tools/rpcctl/rpcctl.py.orig nfs-utils-2.3.3/tools/rpcctl/rpcctl.py
--- nfs-utils-2.3.3/tools/rpcctl/rpcctl.py.orig 2022-06-27 13:22:19.844747880 -0400
+++ nfs-utils-2.3.3/tools/rpcctl/rpcctl.py 2022-06-27 13:23:02.168004219 -0400
@@ -213,7 +213,7 @@ class RpcClient:
def __init__(self, path):
self.path = path
self.name = path.stem
- self.switch = XprtSwitch(path / (path / "switch").readlink(), sep=",")
+ self.switch = XprtSwitch(path / os.readlink(path / "switch"), sep=",")
def __lt__(self, rhs):
return self.name < rhs.name

View File

@ -0,0 +1,34 @@
From 2fdd10bebf395b51e931a10adbdc85f3a3f8a285 Mon Sep 17 00:00:00 2001
From: Alice Mitchell <ajmitchell@redhat.com>
Date: Thu, 23 Jun 2022 16:04:45 +0100
Subject: [PATCH] Remove subparser required option as that was added in py3.7
---
tools/rpcctl/rpcctl.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tools/rpcctl/rpcctl.py b/tools/rpcctl/rpcctl.py
index d2110ad6..2ac6ede9 100755
--- a/tools/rpcctl/rpcctl.py
+++ b/tools/rpcctl/rpcctl.py
@@ -120,7 +120,7 @@ class Xprt:
set = subparser.add_parser("set", help="Change an xprt property")
set.add_argument("xprt", metavar="XPRT", nargs=1,
help="Name of a specific xprt to modify")
- subparser = set.add_subparsers(required=True)
+ subparser = set.add_subparsers()
online = subparser.add_parser("online", help="Set an xprt online")
online.set_defaults(func=Xprt.set_property, property="online")
offline = subparser.add_parser("offline", help="Set an xprt offline")
@@ -185,7 +185,7 @@ class XprtSwitch:
set = subparser.add_parser("set", help="Change an xprt switch property")
set.add_argument("switch", metavar="SWITCH", nargs=1,
help="Name of a specific xprt switch to modify")
- subparser = set.add_subparsers(required=True)
+ subparser = set.add_subparsers()
dstaddr = subparser.add_parser("dstaddr", help="Change an xprt switch's dstaddr")
dstaddr.add_argument("newaddr", metavar="NEWADDR", nargs=1,
help="The new address for the xprt switch")
--
2.36.1

View File

@ -0,0 +1,384 @@
diff --git a/configure.ac b/configure.ac
index f2f2303b..6d464ac5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -639,6 +639,7 @@ AC_CONFIG_FILES([
tools/rpcgen/Makefile
tools/mountstats/Makefile
tools/nfs-iostat/Makefile
+ tools/rpcctl/Makefile
tools/nfsdclnts/Makefile
tools/nfsconf/Makefile
tools/nfsdclddb/Makefile
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 9b4b0803..c3feabbe 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -12,6 +12,6 @@ if CONFIG_NFSDCLD
OPTDIRS += nfsdclddb
endif
-SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat nfsdclnts $(OPTDIRS)
+SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat rpcctl nfsdclnts $(OPTDIRS)
MAINTAINERCLEANFILES = Makefile.in
diff --git a/tools/rpcctl/Makefile.am b/tools/rpcctl/Makefile.am
new file mode 100644
index 00000000..33fb431f
--- /dev/null
+++ b/tools/rpcctl/Makefile.am
@@ -0,0 +1,13 @@
+## Process this file with automake to produce Makefile.in
+PYTHON_FILES = rpcctl.py
+
+man8_MANS = rpcctl.man
+
+EXTRA_DIST = $(man8_MANS) $(PYTHON_FILES)
+
+all-local: $(PYTHON_FILES)
+
+install-data-hook:
+ $(INSTALL) -m 755 rpcctl.py $(DESTDIR)$(sbindir)/rpcctl
+
+MAINTAINERCLEANFILES=Makefile.in
diff --git a/tools/rpcctl/rpcctl.man b/tools/rpcctl/rpcctl.man
new file mode 100644
index 00000000..b87ba0df
--- /dev/null
+++ b/tools/rpcctl/rpcctl.man
@@ -0,0 +1,67 @@
+.\"
+.\" rpcctl(8)
+.\"
+.TH rpcctl 8 "15 Feb 2022"
+.SH NAME
+rpcctl \- Displays SunRPC connection information
+.SH SYNOPSIS
+.nf
+.BR rpcctl " [ \fB\-h \fR| \fB\-\-help \fR] { \fBclient \fR| \fBswitch \fR| \fBxprt \fR}"
+.P
+.BR "rpcctl client" " \fR[ \fB\-h \fR| \fB\-\-help \fR] { \fBshow \fR}"
+.BR "rpcctl client show " "\fR[ \fB\-h \f| \fB\-\-help \fR] [ \fIXPRT \fR]"
+.P
+.BR "rpcctl switch" " \fR[ \fB\-h \fR| \fB\-\-help \fR] { \fBset \fR| \fBshow \fR}"
+.BR "rpcctl switch set" " \fR[ \fB\-h \fR| \fB\-\-help \fR] \fISWITCH \fBdstaddr \fINEWADDR"
+.BR "rpcctl switch show" " \fR[ \fB\-h \fR| \fB\-\-help \fR] [ \fISWITCH \fR]"
+.P
+.BR "rpcctl xprt" " \fR[ \fB\-h \fR| \fB\-\-help \fR] { \fBremove \fR| \fBset \fR| \fBshow \fR}"
+.BR "rpcctl xprt remove" " \fR[ \fB\-h \fR| \fB\-\-help \fR] \fIXPRT"
+.BR "rpcctl xprt set" " \fR[ \fB\-h \fR| \fB\-\-help \fR] \fIXPRT \fR{ \fBdstaddr \fINEWADDR \fR| \fBoffline \fR| \fBonline \fR}"
+.BR "rpcctl xprt show" " \fR[ \fB\-h \fR| \fB\-\-help \fR] [ \fIXPRT \fR]"
+.fi
+.SH DESCRIPTION
+.RB "The " rpcctl " command displays information collected in the SunRPC sysfs files about the system's SunRPC objects.
+.P
+.SS rpcctl client \fR- \fBCommands operating on RPC clients
+.IP "\fBshow \fR[ \fICLIENT \fR] \fB(default)"
+Show detailed information about the RPC clients on this system.
+If \fICLIENT \fRwas provided, then only show information about a single RPC client.
+.P
+.SS rpcctl switch \fR- \fBCommands operating on groups of transports
+.IP "\fBset \fISWITCH \fBdstaddr \fINEWADDR"
+Change the destination address of all transports in the \fISWITCH \fRto \fINEWADDR\fR.
+\fINEWADDR \fRcan be an IP address, DNS name, or anything else resolvable by \fBgethostbyname\fR(3).
+.IP "\fBshow \fR[ \fISWITCH \fR] \fB(default)"
+Show detailed information about groups of transports on this system.
+If \fISWITCH \fRwas provided, then only show information about a single transport group.
+.P
+.SS rpcctl xprt \fR- \fBCommands operating on individual transports
+.IP "\fBremove \fIXPRT"
+Removes the specified \fIXPRT \fRfrom the system.
+Note that "main" transports cannot be removed.
+.P
+.IP "\fBset \fIXPRT \fBdstaddr \fINEWADDR"
+Change the destination address of the specified \fIXPRT \fR to \fINEWADDR\fR.
+\fINEWADDR \fRcan be an IP address, DNS name, or anything else resolvable by \fBgethostbyname\fR(3).
+.P
+.IP "\fBset \fIXPRT \fBoffline"
+Sets the specified \fIXPRT\fR's state to offline.
+.P
+.IP "\fBset \fIXPRT \fBonline"
+Sets the specified \fIXPRT\fR's state to online.
+.IP "\fBshow \fR[ \fIXPRT \fR] \fB(default)"
+Show detailed information about this system's transports.
+If \fIXPRT \fRwas provided, then only show information about a single transport.
+.SH EXAMPLES
+.IP "\fBrpcctl switch show switch-2"
+Show details about the RPC switch named "switch-2".
+.IP "\fBrpcctl xprt remove xprt-4"
+Remove the xprt named "xprt-4" from the system.
+.IP "\fBrpcctl xprt set xprt-3 dstaddr https://linux-nfs.org
+Change the dstaddr of the xprt named "xprt-3" to point to linux-nfs.org
+.SH DIRECTORY
+.TP
+.B /sys/kernel/sunrpc/
+.SH AUTHOR
+Anna Schumaker <Anna.Schumaker@Netapp.com>
diff --git a/tools/rpcctl/rpcctl.py b/tools/rpcctl/rpcctl.py
new file mode 100755
index 00000000..d2110ad6
--- /dev/null
+++ b/tools/rpcctl/rpcctl.py
@@ -0,0 +1,262 @@
+#!/usr/bin/python3
+import argparse
+import collections
+import errno
+import os
+import pathlib
+import socket
+import sys
+
+with open("/proc/mounts", 'r') as f:
+ mount = [ line.split()[1] for line in f if "sysfs" in line ]
+ if len(mount) == 0:
+ print("ERROR: sysfs is not mounted")
+ sys.exit(1)
+
+sunrpc = pathlib.Path(mount[0]) / "kernel" / "sunrpc"
+if not sunrpc.is_dir():
+ print("ERROR: sysfs does not have sunrpc directory")
+ sys.exit(1)
+
+def read_addr_file(path):
+ try:
+ with open(path, 'r') as f:
+ return f.readline().strip()
+ except:
+ return "(enoent)"
+
+def write_addr_file(path, newaddr):
+ with open(path, 'w') as f:
+ f.write(newaddr)
+ return read_addr_file(path)
+
+def read_info_file(path):
+ res = collections.defaultdict(int)
+ try:
+ with open(path) as info:
+ lines = [ l.split("=", 1) for l in info if "=" in l ]
+ res.update({ key:int(val.strip()) for (key, val) in lines })
+ finally:
+ return res
+
+
+class Xprt:
+ def __init__(self, path):
+ self.path = path
+ self.name = path.stem.rsplit("-", 1)[0]
+ self.type = path.stem.split("-")[2]
+ self.info = read_info_file(path / "xprt_info")
+ self.dstaddr = read_addr_file(path / "dstaddr")
+ self.srcaddr = read_addr_file(path / "srcaddr")
+ self.read_state()
+
+ def __lt__(self, rhs):
+ return self.name < rhs.name
+
+ def _xprt(self):
+ main = ", main" if self.info.get("main_xprt") else ""
+ return f"{self.name}: {self.type}, {self.dstaddr}, " \
+ f"port {self.info['dst_port']}, state <{self.state}>{main}"
+
+ def _src_reqs(self):
+ return f" Source: {self.srcaddr}, port {self.info['src_port']}, " \
+ f"Requests: {self.info['num_reqs']}"
+
+ def _cong_slots(self):
+ return f" Congestion: cur {self.info['cur_cong']}, win {self.info['cong_win']}, " \
+ f"Slots: min {self.info['min_num_slots']}, max {self.info['max_num_slots']}"
+
+ def _queues(self):
+ return f" Queues: binding {self.info['binding_q_len']}, " \
+ f"sending {self.info['sending_q_len']}, pending {self.info['pending_q_len']}, " \
+ f"backlog {self.info['backlog_q_len']}, tasks {self.info['tasks_queuelen']}"
+
+ def __str__(self):
+ if not self.path.exists():
+ return f"{self.name}: has been removed"
+ return "\n".join([self._xprt(), self._src_reqs(),
+ self._cong_slots(), self._queues() ])
+
+ def read_state(self):
+ if self.path.exists():
+ with open(self.path / "xprt_state") as f:
+ self.state = ','.join(f.readline().split()[1:])
+
+ def small_str(self):
+ main = " [main]" if self.info.get("main_xprt") else ""
+ return f"{self.name}: {self.type}, {self.dstaddr}{main}"
+
+ def set_dstaddr(self, newaddr):
+ self.dstaddr = write_addr_file(self.path / "dstaddr", newaddr)
+
+ def set_state(self, state):
+ if self.info.get("main_xprt"):
+ raise Exception(f"Main xprts cannot be set {state}")
+ with open(self.path / "xprt_state", 'w') as f:
+ f.write(state)
+ self.read_state()
+
+ def remove(self):
+ if self.info.get("main_xprt"):
+ raise Exception("Main xprts cannot be removed")
+ self.set_state("offline")
+ self.set_state("remove")
+
+ def add_command(subparser):
+ parser = subparser.add_parser("xprt", help="Commands for individual xprts")
+ parser.set_defaults(func=Xprt.show, xprt=None)
+ subparser = parser.add_subparsers()
+
+ remove = subparser.add_parser("remove", help="Remove an xprt")
+ remove.add_argument("xprt", metavar="XPRT", nargs=1,
+ help="Name of the xprt to remove")
+ remove.set_defaults(func=Xprt.set_property, property="remove")
+
+ show = subparser.add_parser("show", help="Show xprts")
+ show.add_argument("xprt", metavar="XPRT", nargs='?',
+ help="Name of a specific xprt to show")
+ show.set_defaults(func=Xprt.show)
+
+ set = subparser.add_parser("set", help="Change an xprt property")
+ set.add_argument("xprt", metavar="XPRT", nargs=1,
+ help="Name of a specific xprt to modify")
+ subparser = set.add_subparsers(required=True)
+ online = subparser.add_parser("online", help="Set an xprt online")
+ online.set_defaults(func=Xprt.set_property, property="online")
+ offline = subparser.add_parser("offline", help="Set an xprt offline")
+ offline.set_defaults(func=Xprt.set_property, property="offline")
+ dstaddr = subparser.add_parser("dstaddr", help="Change an xprt's dstaddr")
+ dstaddr.add_argument("newaddr", metavar="NEWADDR", nargs=1,
+ help="The new address for the xprt")
+ dstaddr.set_defaults(func=Xprt.set_property, property="dstaddr")
+
+ def get_by_name(name):
+ glob = f"**/{name}-*" if name else "**/xprt-*"
+ res = [ Xprt(x) for x in (sunrpc / "xprt-switches").glob(glob) ]
+ if name and len(res) == 0:
+ raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT),
+ f"{sunrpc / 'xprt-switches' / glob}")
+ return sorted(res)
+
+ def show(args):
+ for xprt in Xprt.get_by_name(args.xprt):
+ print(xprt)
+
+ def set_property(args):
+ for xprt in Xprt.get_by_name(args.xprt[0]):
+ if args.property == "dstaddr":
+ xprt.set_dstaddr(socket.gethostbyname(args.newaddr[0]))
+ elif args.property == "remove":
+ xprt.remove()
+ else:
+ xprt.set_state(args.property)
+ print(xprt)
+
+
+class XprtSwitch:
+ def __init__(self, path, sep=":"):
+ self.path = path
+ self.name = path.stem
+ self.info = read_info_file(path / "xprt_switch_info")
+ self.xprts = sorted([ Xprt(p) for p in self.path.iterdir() if p.is_dir() ])
+ self.sep = sep
+
+ def __lt__(self, rhs):
+ return self.name < rhs.name
+
+ def __str__(self):
+ switch = f"{self.name}{self.sep} " \
+ f"xprts {self.info['num_xprts']}, " \
+ f"active {self.info['num_active']}, " \
+ f"queue {self.info['queue_len']}"
+ xprts = [ f" {x.small_str()}" for x in self.xprts ]
+ return "\n".join([ switch ] + xprts)
+
+ def add_command(subparser):
+ parser = subparser.add_parser("switch", help="Commands for xprt switches")
+ parser.set_defaults(func=XprtSwitch.show, switch=None)
+ subparser = parser.add_subparsers()
+
+ show = subparser.add_parser("show", help="Show xprt switches")
+ show.add_argument("switch", metavar="SWITCH", nargs='?',
+ help="Name of a specific switch to show")
+ show.set_defaults(func=XprtSwitch.show)
+
+ set = subparser.add_parser("set", help="Change an xprt switch property")
+ set.add_argument("switch", metavar="SWITCH", nargs=1,
+ help="Name of a specific xprt switch to modify")
+ subparser = set.add_subparsers(required=True)
+ dstaddr = subparser.add_parser("dstaddr", help="Change an xprt switch's dstaddr")
+ dstaddr.add_argument("newaddr", metavar="NEWADDR", nargs=1,
+ help="The new address for the xprt switch")
+ dstaddr.set_defaults(func=XprtSwitch.set_property, property="dstaddr")
+
+ def get_by_name(name):
+ xprt_switches = sunrpc / "xprt-switches"
+ if name:
+ return [ XprtSwitch(xprt_switches / name) ]
+ return [ XprtSwitch(f) for f in sorted(xprt_switches.iterdir()) ]
+
+ def show(args):
+ for switch in XprtSwitch.get_by_name(args.switch):
+ print(switch)
+
+ def set_property(args):
+ for switch in XprtSwitch.get_by_name(args.switch[0]):
+ resolved = socket.gethostbyname(args.newaddr[0])
+ for xprt in switch.xprts:
+ xprt.set_dstaddr(resolved)
+ print(switch)
+
+
+class RpcClient:
+ def __init__(self, path):
+ self.path = path
+ self.name = path.stem
+ self.switch = XprtSwitch(path / (path / "switch").readlink(), sep=",")
+
+ def __lt__(self, rhs):
+ return self.name < rhs.name
+
+ def __str__(self):
+ return f"{self.name}: {self.switch}"
+
+ def add_command(subparser):
+ parser = subparser.add_parser("client", help="Commands for rpc clients")
+ parser.set_defaults(func=RpcClient.show, client=None)
+ subparser = parser.add_subparsers()
+
+ show = subparser.add_parser("show", help="Show rpc clients")
+ show.add_argument("client", metavar="CLIENT", nargs='?',
+ help="Name of a specific rpc client to show")
+ parser.set_defaults(func=RpcClient.show)
+
+ def get_by_name(name):
+ rpc_clients = sunrpc / "rpc-clients"
+ if name:
+ return [ RpcClient(rpc_clients / name) ]
+ return [ RpcClient(f) for f in sorted(rpc_clients.iterdir()) ]
+
+ def show(args):
+ for client in RpcClient.get_by_name(args.client):
+ print(client)
+
+
+parser = argparse.ArgumentParser()
+
+def show_small_help(args):
+ parser.print_usage()
+ print("sunrpc dir:", sunrpc)
+parser.set_defaults(func=show_small_help)
+
+subparser = parser.add_subparsers(title="commands")
+RpcClient.add_command(subparser)
+XprtSwitch.add_command(subparser)
+Xprt.add_command(subparser)
+
+args = parser.parse_args()
+try:
+ args.func(args)
+except Exception as e:
+ print(str(e))
+ sys.exit(1)

View File

@ -0,0 +1,37 @@
commit 003000d451833309c963054e58a48fa1df7e767b
Author: Steve Dickson <steved@redhat.com>
Date: Thu Dec 10 13:13:03 2020 -0500
exportfs: Ingnore export failures in nfs-server.serivce unit
With some recent commits, exportfs will continue on trying to
export filesystems even when an entry is invalid or does
not exist, but will still have a non-zero exit to report
the error.
This situation should not stop the nfs-server service
from comingup so nfs-server.service file should
ignore these types of failures
Signed-off-by: Steve Dickson <steved@redhat.com>
diff --git a/systemd/nfs-server.service b/systemd/nfs-server.service
index 06c1adb7..b432f910 100644
--- a/systemd/nfs-server.service
+++ b/systemd/nfs-server.service
@@ -21,13 +21,13 @@ After=rpc-gssd.service gssproxy.service rpc-svcgssd.service
[Service]
Type=oneshot
RemainAfterExit=yes
-ExecStartPre=/usr/sbin/exportfs -r
+ExecStartPre=-/usr/sbin/exportfs -r
ExecStart=/usr/sbin/rpc.nfsd
ExecStop=/usr/sbin/rpc.nfsd 0
ExecStopPost=/usr/sbin/exportfs -au
ExecStopPost=/usr/sbin/exportfs -f
-ExecReload=/usr/sbin/exportfs -r
+ExecReload=-/usr/sbin/exportfs -r
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,13 @@
diff -up nfs-utils-2.3.3/systemd/rpc-statd.service.orig nfs-utils-2.3.3/systemd/rpc-statd.service
--- nfs-utils-2.3.3/systemd/rpc-statd.service.orig 2018-09-06 14:09:08.000000000 -0400
+++ nfs-utils-2.3.3/systemd/rpc-statd.service 2022-08-02 11:02:44.327397404 -0400
@@ -4,7 +4,8 @@ DefaultDependencies=no
Conflicts=umount.target
Requires=nss-lookup.target rpcbind.socket
Wants=network-online.target
-After=network-online.target nss-lookup.target rpcbind.socket
+Wants=rpc-statd-notify.service
+After=network-online.target nss-lookup.target rpcbind.service
PartOf=nfs-utils.service

View File

@ -2,7 +2,7 @@ Summary: NFS utilities and supporting clients and daemons for the kernel NFS ser
Name: nfs-utils
URL: http://linux-nfs.org/
Version: 2.3.3
Release: 35%{?dist}
Release: 59%{?dist}
Epoch: 1
# group all 32bit related archs
@ -61,6 +61,54 @@ Patch027: nfs-utils-2.3.3-nconnect-manpage.patch
Patch028: nfs-utils-2.3.3-nfsdclddb-rename.patch
Patch029: nfs-utils-2.3.3-nfsclnts-cmd.patch
#
# RHEL 8.4
#
Patch030: nfs-utils-2.3.3-exportfs-man-labels.patch
Patch031: nfs-utils-2.3.3-nfsiostat-div-zero.patch
Patch032: nfs-utils-2.3.3-nfsiostat-key-error.patch
Patch033: nfs-utils-2.3.3-nfsdclddb-manpage-rename.patch
Patch034: nfs-utils-2.3.3-systemd-exportfs-nofail.patch
Patch035: nfs-utils-2.3.3-exports-manpage-outdated.patch
Patch036: nfs-utils-2.3.3-gssd-multithread-updates.patch
Patch037: nfs-utils-2.3.3-mountd-pseudofs.patch
#
# RHEL 8.5
#
Patch038: nfs-utils-2.3.3-gssd-k5identity.patch
Patch039: nfs-utils-2.3.3-gssd-man-tflag.patch
Patch040: nfs-utils-2.3.3-exportfs-root.patch
Patch041: nfs-utils-2.3.3-mount-sloppy.patch
Patch042: nfs-utils-2.3.3-gssd-failed-thread.patch
Patch043: nfs-utils-2.3.3-gssd-timeout-thread.patch
Patch044: nfs-utils-2.3.3-gssd-debug-cleanup.patch
Patch045: nfs-utils-2.3.3-gssd-mutex-refcnt.patch
#
# RHEL 8.6
#
Patch046: nfs-utils-2.3.3-mountd-v4-logging.patch
Patch047: nfs-utils-2.3.3-gssd-printerr.patch
Patch048: nfs-utils-2.3.3-mount-ebusy.patch
Patch049: nfs-utils-2.3.3-nfsidmap-debug.patch
#
# RHEL 8.7
#
Patch050: nfs-utils-2.3.3-nfsman-softreval.patch
Patch051: nfs-utils-2.3.3-rpcctl.patch
Patch052: nfs-utils-2.3.3-nfsrahead.patch
Patch053: nfs-utils-2.3.3-rpcctl-subparser.patch
Patch054: nfs-utils-2.3.3-rpcctl-posixpath.patch
Patch055: nfs-utils-2.3.3-systemd-rpcstatd.patch
#
# rhel 8.8
#
Patch056: nfs-utils-2.3.3-mountd-v4clnts.patch
Patch057: nfs-utils-2.3.3-covscan-return-value.patch
Patch100: nfs-utils-1.2.1-statdpath-man.patch
Patch101: nfs-utils-1.2.1-exp-subtree-warn-off.patch
Patch102: nfs-utils-2.3.3-idmap-errmsg.patch
@ -128,16 +176,8 @@ developing programs which use the libnfsidmap library.
%description
The nfs-utils package provides a daemon for the kernel NFS server and
related tools, which provides a much higher level of performance than the
traditional Linux NFS server used by most users.
This package also contains the showmount program. Showmount queries the
mount daemon on a remote host for information about the NFS (Network File
System) server on the remote host. For example, showmount can display the
clients which are mounted on that host.
This package also contains the mount.nfs and umount.nfs program.
The nfs-utils package provides various utilities for use with NFS
clients and servers.
%prep
%autosetup -p1
@ -223,8 +263,6 @@ done
cat /etc/group | cut -d':' -f 1 | grep --quiet rpcuser 2>/dev/null
if [ "$?" -eq 1 ]; then
/usr/sbin/groupadd -g %{rpcuser_uid} rpcuser >/dev/null 2>&1 || :
else
/usr/sbin/groupmod -g %{rpcuser_uid} rpcuser >/dev/null 2>&1 || :
fi
# Create rpcuser uid as long as it does not already exist.
@ -253,11 +291,6 @@ if [ $? -eq 1 ]; then
fi
%post
# Enable nfs-convert so if an old configuration
# exists a conversion will occur
/bin/systemctl enable nfs-convert >/dev/null 2>&1 || :
if [ $1 -eq 1 ] ; then
# Initial installation
/bin/systemctl enable nfs-client.target >/dev/null 2>&1 || :
@ -270,8 +303,6 @@ if [ $1 -eq 0 ]; then
%systemd_preun nfs-client.target
%systemd_preun nfs-server.server
rm -rf /var/lib/nfs/statd
rm -rf /var/lib/nfs/v4recovery
fi
%postun
@ -280,6 +311,11 @@ fi
/bin/systemctl --system daemon-reload >/dev/null 2>&1 || :
if [ $1 -eq 0 ] ; then
rm -rf /var/lib/nfs/statd
rm -rf /var/lib/nfs/v4recovery
fi
%triggerin -- nfs-utils > 1:2.1.1-3
/bin/systemctl try-restart gssproxy || :
@ -324,6 +360,9 @@ fi
%{_sbindir}/nfsdclddb
%{_sbindir}/nfsdcld
%{_sbindir}/nfsdclnts
%{_sbindir}/rpcctl
%{_libexecdir}/nfsrahead
%{_udevrulesdir}/99-nfs.rules
%{_mandir}/*/*
%{_pkgdir}/*/*
@ -347,6 +386,89 @@ fi
%{_libdir}/libnfsidmap.so
%changelog
* Thu Jan 12 2023 Steve Dickson <steved@redhat.com> 2.3.3-59
- Covscan Scan: Wrong Check of Return Value (bz 2151966)
- Covscan Scan: Clang (experimental) (bz 2151971)
* Mon Sep 26 2022 Steve Dickson <steved@redhat.com> 2.3.3-58
- mountd: Check 'nfsd/clients' directory presence (bz 2123073)
* Tue Aug 2 2022 Steve Dickson <steved@redhat.com> 2.3.3-57
- rpc-statd.service: Stop rpcbind and rpc.stat in an exit race (bz 2100395)
* Thu Jun 30 2022 Steve Dickson <steved@redhat.com> 2.3.3-56
- rpcctl: 'PosixPath' object has no attribute 'readlink' (bz 2087187)
* Mon Jun 27 2022 Steve Dickson <steved@redhat.com> 2.3.3-55
- rpcctl: Remove subparser required option as that was added in py3.7 (bz 2087187)
* Tue Jun 14 2022 Steve Dickson <steved@redhat.com> 2.3.3-54
- Create the nfsrahead command (bz 1946283)
* Tue May 31 2022 Steve Dickson <steved@redhat.com> 2.3.3-53
- rpcctl: Add a rpcctl.py tool (bz 2087187)
* Tue May 24 2022 Steve Dickson <steved@redhat.com> 2.3.3-52
- manpage: Add a description of the softreval/nosoftreval (bz 2073476)
* Mon Mar 7 2022 Steve Dickson <steved@redhat.com> 2.3.3-51
- libnfsidmap: Turn off default verbosity (bz 2057612)
* Sat Feb 19 2022 Steve Dickson <steved@redhat.com> 2.3.3-50
- mount.nfs: Fix Typo auto negotiating code. (bz 1946346)
* Mon Feb 14 2022 Steve Dickson <steved@redhat.com> 2.3.3-49
- mount.nfs Fix error reporting for already mounted shares (bz 1946346)
* Thu Nov 4 2021 Steve Dickson <steved@redhat.com> 2.3.3-48
- gssd: fix crash in debug message (bz 1988283)
* Tue Nov 2 2021 Steve Dickson <steved@redhat.com> 2.3.3-47
- Enable logging for NFSv4 mount requests (bz 2004151)
* Wed Jul 28 2021 Steve Dickson <steved@redhat.com> 2.3.3-46
- mount.nfs: Fix the sloppy option processing (bz 1967883)
* Thu Jul 22 2021 Steve Dickson <steved@redhat.com> 2.3.3-45
- gssd: use mutex to protect decrement of refcount (bz 1511706)
* Mon Jul 19 2021 Steve Dickson <steved@redhat.com> 2.3.3-44
- gssd: Deal with failed thread creation (bz 1981400)
- gssd: Add timeout for upcall threads (bz 1981403)
- gssd: Cleaned up debug messages (bz 1961056)
- spec: Updated description of the nfs-utils rpm (bz 1981419)
* Sat Jul 10 2021 Steve Dickson <steved@redhat.com> 2.3.3-43
- mount.nfs: insert 'sloppy' at beginning of the options (bz 1967883)
* Mon May 10 2021 Steve Dickson <steved@redhat.com> 2.3.3-42
- gssd: Add options to allow for the use of ~/.k5identity file (bz 1868087)
- man: Correct gssd(8) description of rpc-timeout and context-timeout (bz 1908232)
- exportfs: fix unexporting of '/' (bz 1944119)
* Wed Jan 20 2021 Steve Dickson <steved@redhat.com> 2.3.3-41
- mountd: never root squash on the pseudofs (bz 1804912)
* Mon Dec 14 2020 Steve Dickson <steved@redhat.com> 2.3.3-40
- gssd: upstream multithreaded updates (bz 1906792)
* Fri Dec 11 2020 Steve Dickson <steved@redhat.com> 2.3.3-39
- systemd: Ingnore export failures in nfs-server.serivce unit (bz 1894873)
- exports.man: Remove some outdated verbiage (bz 1769688)
* Thu Dec 10 2020 Steve Dickson <steved@redhat.com> 2.3.3-38
- exports man page: warn about subdirectory exports (bz 1652437)
- Don't modify /etc/group on upgrades (bz 1856881)
- nfs-iostat: divide by zero with fresh mount (bz 1861823)
- nfsiostat: Drop autofs entries before calling compare_iostats() (bz 1859130)
- nfsdclddb: clddb-tool was recently renamed to nfsdclddb (bz 1893599)
* Thu Dec 10 2020 Alice Mitchell <ajmitchell@redhat.com> 2.3.3-37
- Remove manual enabling of nfs-convert (bz 1683895)
* Fri Oct 9 2020 Alice Mitchell <ajmitchell@redhat.com> 2.3.3-36
- Fix uninstall warnings (bz 1733170)
* Wed Jun 10 2020 Steve Dickson <steved@redhat.com> 2.3.3-35
- Fix dependency problems with nfsdclnts (bz 1841502)