summaryrefslogtreecommitdiffstats
path: root/sys/fs/nfs/nfs_commonsubs.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/fs/nfs/nfs_commonsubs.c')
-rw-r--r--sys/fs/nfs/nfs_commonsubs.c683
1 files changed, 487 insertions, 196 deletions
diff --git a/sys/fs/nfs/nfs_commonsubs.c b/sys/fs/nfs/nfs_commonsubs.c
index 6e0204b8..5b8bca3 100644
--- a/sys/fs/nfs/nfs_commonsubs.c
+++ b/sys/fs/nfs/nfs_commonsubs.c
@@ -44,6 +44,8 @@ __FBSDID("$FreeBSD$");
#include <fs/nfs/nfsport.h>
+#include <security/mac/mac_framework.h>
+
/*
* Data items converted to xdr at startup, since they are constant
* This is kinda hokey, but may save a little time doing byte swaps
@@ -68,6 +70,7 @@ int ncl_mbuf_mlen = MLEN;
int nfsd_enable_stringtouid = 0;
NFSNAMEIDMUTEX;
NFSSOCKMUTEX;
+extern int nfsrv_lughashsize;
/*
* This array of structures indicates, for V4:
@@ -154,11 +157,14 @@ static int nfsrv_usercnt = 0;
static int nfsrv_dnsnamelen;
static u_char *nfsrv_dnsname = NULL;
static int nfsrv_usermax = 999999999;
-static struct nfsuserhashhead nfsuserhash[NFSUSERHASHSIZE];
-static struct nfsuserhashhead nfsusernamehash[NFSUSERHASHSIZE];
-static struct nfsuserhashhead nfsgrouphash[NFSGROUPHASHSIZE];
-static struct nfsuserhashhead nfsgroupnamehash[NFSGROUPHASHSIZE];
-static struct nfsuserlruhead nfsuserlruhead;
+struct nfsrv_lughash {
+ struct mtx mtx;
+ struct nfsuserhashhead lughead;
+};
+static struct nfsrv_lughash *nfsuserhash;
+static struct nfsrv_lughash *nfsusernamehash;
+static struct nfsrv_lughash *nfsgrouphash;
+static struct nfsrv_lughash *nfsgroupnamehash;
/*
* This static array indicates whether or not the RPC generates a large
@@ -177,7 +183,7 @@ static void nfsv4_wanted(struct nfsv4lock *lp);
static int nfsrv_cmpmixedcase(u_char *cp, u_char *cp2, int len);
static int nfsrv_getuser(int procnum, uid_t uid, gid_t gid, char *name,
NFSPROC_T *p);
-static void nfsrv_removeuser(struct nfsusrgrp *usrp);
+static void nfsrv_removeuser(struct nfsusrgrp *usrp, int isuser);
static int nfsrv_getrefstr(struct nfsrv_descript *, u_char **, u_char **,
int *, int *);
static void nfsrv_refstrbigenough(int, u_char **, u_char **, int *);
@@ -2541,18 +2547,17 @@ nfsv4_uidtostr(uid_t uid, u_char **cpp, int *retlenp, NFSPROC_T *p)
u_char *cp = *cpp;
uid_t tmp;
int cnt, hasampersand, len = NFSV4_SMALLSTR, ret;
+ struct nfsrv_lughash *hp;
cnt = 0;
tryagain:
- NFSLOCKNAMEID();
- if (nfsrv_dnsname) {
+ if (nfsrv_dnsnamelen > 0) {
/*
* Always map nfsrv_defaultuid to "nobody".
*/
if (uid == nfsrv_defaultuid) {
i = nfsrv_dnsnamelen + 7;
if (i > len) {
- NFSUNLOCKNAMEID();
if (len > NFSV4_SMALLSTR)
free(cp, M_NFSSTRING);
cp = malloc(i, M_NFSSTRING, M_WAITOK);
@@ -2564,11 +2569,12 @@ tryagain:
NFSBCOPY("nobody@", cp, 7);
cp += 7;
NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
- NFSUNLOCKNAMEID();
return;
}
hasampersand = 0;
- LIST_FOREACH(usrp, NFSUSERHASH(uid), lug_numhash) {
+ hp = NFSUSERHASH(uid);
+ mtx_lock(&hp->mtx);
+ TAILQ_FOREACH(usrp, &hp->lughead, lug_numhash) {
if (usrp->lug_uid == uid) {
if (usrp->lug_expiry < NFSD_MONOSEC)
break;
@@ -2588,7 +2594,7 @@ tryagain:
i = usrp->lug_namelen +
nfsrv_dnsnamelen + 1;
if (i > len) {
- NFSUNLOCKNAMEID();
+ mtx_unlock(&hp->mtx);
if (len > NFSV4_SMALLSTR)
free(cp, M_NFSSTRING);
cp = malloc(i, M_NFSSTRING, M_WAITOK);
@@ -2603,20 +2609,19 @@ tryagain:
*cp++ = '@';
NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
}
- TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
- TAILQ_INSERT_TAIL(&nfsuserlruhead, usrp, lug_lru);
- NFSUNLOCKNAMEID();
+ TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
+ TAILQ_INSERT_TAIL(&hp->lughead, usrp,
+ lug_numhash);
+ mtx_unlock(&hp->mtx);
return;
}
}
- NFSUNLOCKNAMEID();
+ mtx_unlock(&hp->mtx);
cnt++;
ret = nfsrv_getuser(RPCNFSUSERD_GETUID, uid, (gid_t)0,
NULL, p);
if (ret == 0 && cnt < 2)
goto tryagain;
- } else {
- NFSUNLOCKNAMEID();
}
/*
@@ -2640,6 +2645,52 @@ tryagain:
}
/*
+ * Get a credential for the uid with the server's group list.
+ * If none is found, just return the credential passed in after
+ * logging a warning message.
+ */
+struct ucred *
+nfsrv_getgrpscred(struct ucred *oldcred)
+{
+ struct nfsusrgrp *usrp;
+ struct ucred *newcred;
+ int cnt, ret;
+ uid_t uid;
+ struct nfsrv_lughash *hp;
+
+ cnt = 0;
+ uid = oldcred->cr_uid;
+tryagain:
+ if (nfsrv_dnsnamelen > 0) {
+ hp = NFSUSERHASH(uid);
+ mtx_lock(&hp->mtx);
+ TAILQ_FOREACH(usrp, &hp->lughead, lug_numhash) {
+ if (usrp->lug_uid == uid) {
+ if (usrp->lug_expiry < NFSD_MONOSEC)
+ break;
+ if (usrp->lug_cred != NULL) {
+ newcred = crhold(usrp->lug_cred);
+ crfree(oldcred);
+ } else
+ newcred = oldcred;
+ TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
+ TAILQ_INSERT_TAIL(&hp->lughead, usrp,
+ lug_numhash);
+ mtx_unlock(&hp->mtx);
+ return (newcred);
+ }
+ }
+ mtx_unlock(&hp->mtx);
+ cnt++;
+ ret = nfsrv_getuser(RPCNFSUSERD_GETUID, uid, (gid_t)0,
+ NULL, curthread);
+ if (ret == 0 && cnt < 2)
+ goto tryagain;
+ }
+ return (oldcred);
+}
+
+/*
* Convert a string to a uid.
* If no conversion is possible return NFSERR_BADOWNER, otherwise
* return 0.
@@ -2657,6 +2708,7 @@ nfsv4_strtouid(struct nfsrv_descript *nd, u_char *str, int len, uid_t *uidp,
int cnt, ret;
int error = 0;
uid_t tuid;
+ struct nfsrv_lughash *hp, *hp2;
if (len == 0) {
error = NFSERR_BADOWNER;
@@ -2686,49 +2738,55 @@ nfsv4_strtouid(struct nfsrv_descript *nd, u_char *str, int len, uid_t *uidp,
cnt = 0;
tryagain:
- NFSLOCKNAMEID();
- /*
- * If an '@' is found and the domain name matches, search for the name
- * with dns stripped off.
- * Mixed case alpahbetics will match for the domain name, but all
- * upper case will not.
- */
- if (cnt == 0 && i < len && i > 0 && nfsrv_dnsname &&
- (len - 1 - i) == nfsrv_dnsnamelen &&
- !nfsrv_cmpmixedcase(cp, nfsrv_dnsname, nfsrv_dnsnamelen)) {
- len -= (nfsrv_dnsnamelen + 1);
- *(cp - 1) = '\0';
- }
-
- /*
- * Check for the special case of "nobody".
- */
- if (len == 6 && !NFSBCMP(str, "nobody", 6)) {
- *uidp = nfsrv_defaultuid;
- NFSUNLOCKNAMEID();
- error = 0;
- goto out;
- }
-
- LIST_FOREACH(usrp, NFSUSERNAMEHASH(str, len), lug_namehash) {
- if (usrp->lug_namelen == len &&
- !NFSBCMP(usrp->lug_name, str, len)) {
- if (usrp->lug_expiry < NFSD_MONOSEC)
- break;
- *uidp = usrp->lug_uid;
- TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
- TAILQ_INSERT_TAIL(&nfsuserlruhead, usrp, lug_lru);
- NFSUNLOCKNAMEID();
+ if (nfsrv_dnsnamelen > 0) {
+ /*
+ * If an '@' is found and the domain name matches, search for
+ * the name with dns stripped off.
+ * Mixed case alpahbetics will match for the domain name, but
+ * all upper case will not.
+ */
+ if (cnt == 0 && i < len && i > 0 &&
+ (len - 1 - i) == nfsrv_dnsnamelen &&
+ !nfsrv_cmpmixedcase(cp, nfsrv_dnsname, nfsrv_dnsnamelen)) {
+ len -= (nfsrv_dnsnamelen + 1);
+ *(cp - 1) = '\0';
+ }
+
+ /*
+ * Check for the special case of "nobody".
+ */
+ if (len == 6 && !NFSBCMP(str, "nobody", 6)) {
+ *uidp = nfsrv_defaultuid;
error = 0;
goto out;
}
+
+ hp = NFSUSERNAMEHASH(str, len);
+ mtx_lock(&hp->mtx);
+ TAILQ_FOREACH(usrp, &hp->lughead, lug_namehash) {
+ if (usrp->lug_namelen == len &&
+ !NFSBCMP(usrp->lug_name, str, len)) {
+ if (usrp->lug_expiry < NFSD_MONOSEC)
+ break;
+ hp2 = NFSUSERHASH(usrp->lug_uid);
+ mtx_lock(&hp2->mtx);
+ TAILQ_REMOVE(&hp2->lughead, usrp, lug_numhash);
+ TAILQ_INSERT_TAIL(&hp2->lughead, usrp,
+ lug_numhash);
+ *uidp = usrp->lug_uid;
+ mtx_unlock(&hp2->mtx);
+ mtx_unlock(&hp->mtx);
+ error = 0;
+ goto out;
+ }
+ }
+ mtx_unlock(&hp->mtx);
+ cnt++;
+ ret = nfsrv_getuser(RPCNFSUSERD_GETUSER, (uid_t)0, (gid_t)0,
+ str, p);
+ if (ret == 0 && cnt < 2)
+ goto tryagain;
}
- NFSUNLOCKNAMEID();
- cnt++;
- ret = nfsrv_getuser(RPCNFSUSERD_GETUSER, (uid_t)0, (gid_t)0,
- str, p);
- if (ret == 0 && cnt < 2)
- goto tryagain;
error = NFSERR_BADOWNER;
out:
@@ -2751,18 +2809,17 @@ nfsv4_gidtostr(gid_t gid, u_char **cpp, int *retlenp, NFSPROC_T *p)
u_char *cp = *cpp;
gid_t tmp;
int cnt, hasampersand, len = NFSV4_SMALLSTR, ret;
+ struct nfsrv_lughash *hp;
cnt = 0;
tryagain:
- NFSLOCKNAMEID();
- if (nfsrv_dnsname) {
+ if (nfsrv_dnsnamelen > 0) {
/*
* Always map nfsrv_defaultgid to "nogroup".
*/
if (gid == nfsrv_defaultgid) {
i = nfsrv_dnsnamelen + 8;
if (i > len) {
- NFSUNLOCKNAMEID();
if (len > NFSV4_SMALLSTR)
free(cp, M_NFSSTRING);
cp = malloc(i, M_NFSSTRING, M_WAITOK);
@@ -2774,11 +2831,12 @@ tryagain:
NFSBCOPY("nogroup@", cp, 8);
cp += 8;
NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
- NFSUNLOCKNAMEID();
return;
}
hasampersand = 0;
- LIST_FOREACH(usrp, NFSGROUPHASH(gid), lug_numhash) {
+ hp = NFSGROUPHASH(gid);
+ mtx_lock(&hp->mtx);
+ TAILQ_FOREACH(usrp, &hp->lughead, lug_numhash) {
if (usrp->lug_gid == gid) {
if (usrp->lug_expiry < NFSD_MONOSEC)
break;
@@ -2798,7 +2856,7 @@ tryagain:
i = usrp->lug_namelen +
nfsrv_dnsnamelen + 1;
if (i > len) {
- NFSUNLOCKNAMEID();
+ mtx_unlock(&hp->mtx);
if (len > NFSV4_SMALLSTR)
free(cp, M_NFSSTRING);
cp = malloc(i, M_NFSSTRING, M_WAITOK);
@@ -2813,20 +2871,19 @@ tryagain:
*cp++ = '@';
NFSBCOPY(nfsrv_dnsname, cp, nfsrv_dnsnamelen);
}
- TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
- TAILQ_INSERT_TAIL(&nfsuserlruhead, usrp, lug_lru);
- NFSUNLOCKNAMEID();
+ TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
+ TAILQ_INSERT_TAIL(&hp->lughead, usrp,
+ lug_numhash);
+ mtx_unlock(&hp->mtx);
return;
}
}
- NFSUNLOCKNAMEID();
+ mtx_unlock(&hp->mtx);
cnt++;
ret = nfsrv_getuser(RPCNFSUSERD_GETGID, (uid_t)0, gid,
NULL, p);
if (ret == 0 && cnt < 2)
goto tryagain;
- } else {
- NFSUNLOCKNAMEID();
}
/*
@@ -2867,6 +2924,7 @@ nfsv4_strtogid(struct nfsrv_descript *nd, u_char *str, int len, gid_t *gidp,
int cnt, ret;
int error = 0;
gid_t tgid;
+ struct nfsrv_lughash *hp, *hp2;
if (len == 0) {
error = NFSERR_BADOWNER;
@@ -2896,47 +2954,53 @@ nfsv4_strtogid(struct nfsrv_descript *nd, u_char *str, int len, gid_t *gidp,
cnt = 0;
tryagain:
- NFSLOCKNAMEID();
- /*
- * If an '@' is found and the dns name matches, search for the name
- * with the dns stripped off.
- */
- if (cnt == 0 && i < len && i > 0 && nfsrv_dnsname &&
- (len - 1 - i) == nfsrv_dnsnamelen &&
- !nfsrv_cmpmixedcase(cp, nfsrv_dnsname, nfsrv_dnsnamelen)) {
- len -= (nfsrv_dnsnamelen + 1);
- *(cp - 1) = '\0';
- }
-
- /*
- * Check for the special case of "nogroup".
- */
- if (len == 7 && !NFSBCMP(str, "nogroup", 7)) {
- *gidp = nfsrv_defaultgid;
- NFSUNLOCKNAMEID();
- error = 0;
- goto out;
- }
-
- LIST_FOREACH(usrp, NFSGROUPNAMEHASH(str, len), lug_namehash) {
- if (usrp->lug_namelen == len &&
- !NFSBCMP(usrp->lug_name, str, len)) {
- if (usrp->lug_expiry < NFSD_MONOSEC)
- break;
- *gidp = usrp->lug_gid;
- TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
- TAILQ_INSERT_TAIL(&nfsuserlruhead, usrp, lug_lru);
- NFSUNLOCKNAMEID();
+ if (nfsrv_dnsnamelen > 0) {
+ /*
+ * If an '@' is found and the dns name matches, search for the
+ * name with the dns stripped off.
+ */
+ if (cnt == 0 && i < len && i > 0 &&
+ (len - 1 - i) == nfsrv_dnsnamelen &&
+ !nfsrv_cmpmixedcase(cp, nfsrv_dnsname, nfsrv_dnsnamelen)) {
+ len -= (nfsrv_dnsnamelen + 1);
+ *(cp - 1) = '\0';
+ }
+
+ /*
+ * Check for the special case of "nogroup".
+ */
+ if (len == 7 && !NFSBCMP(str, "nogroup", 7)) {
+ *gidp = nfsrv_defaultgid;
error = 0;
goto out;
}
+
+ hp = NFSGROUPNAMEHASH(str, len);
+ mtx_lock(&hp->mtx);
+ TAILQ_FOREACH(usrp, &hp->lughead, lug_namehash) {
+ if (usrp->lug_namelen == len &&
+ !NFSBCMP(usrp->lug_name, str, len)) {
+ if (usrp->lug_expiry < NFSD_MONOSEC)
+ break;
+ hp2 = NFSGROUPHASH(usrp->lug_gid);
+ mtx_lock(&hp2->mtx);
+ TAILQ_REMOVE(&hp2->lughead, usrp, lug_numhash);
+ TAILQ_INSERT_TAIL(&hp2->lughead, usrp,
+ lug_numhash);
+ *gidp = usrp->lug_gid;
+ mtx_unlock(&hp2->mtx);
+ mtx_unlock(&hp->mtx);
+ error = 0;
+ goto out;
+ }
+ }
+ mtx_unlock(&hp->mtx);
+ cnt++;
+ ret = nfsrv_getuser(RPCNFSUSERD_GETGROUP, (uid_t)0, (gid_t)0,
+ str, p);
+ if (ret == 0 && cnt < 2)
+ goto tryagain;
}
- NFSUNLOCKNAMEID();
- cnt++;
- ret = nfsrv_getuser(RPCNFSUSERD_GETGROUP, (uid_t)0, (gid_t)0,
- str, p);
- if (ret == 0 && cnt < 2)
- goto tryagain;
error = NFSERR_BADOWNER;
out:
@@ -3094,111 +3158,218 @@ APPLESTATIC int
nfssvc_idname(struct nfsd_idargs *nidp)
{
struct nfsusrgrp *nusrp, *usrp, *newusrp;
- struct nfsuserhashhead *hp;
- int i;
+ struct nfsrv_lughash *hp_name, *hp_idnum, *thp;
+ int i, group_locked, groupname_locked, user_locked, username_locked;
int error = 0;
u_char *cp;
+ gid_t *grps;
+ struct ucred *cr;
+ static int onethread = 0;
+ static time_t lasttime = 0;
if (nidp->nid_flag & NFSID_INITIALIZE) {
- cp = (u_char *)malloc(nidp->nid_namelen + 1,
- M_NFSSTRING, M_WAITOK);
- error = copyin(CAST_USER_ADDR_T(nidp->nid_name), cp,
- nidp->nid_namelen);
- NFSLOCKNAMEID();
- if (nfsrv_dnsname) {
+ cp = malloc(nidp->nid_namelen + 1, M_NFSSTRING, M_WAITOK);
+ error = copyin(CAST_USER_ADDR_T(nidp->nid_name), cp,
+ nidp->nid_namelen);
+ if (error != 0) {
+ free(cp, M_NFSSTRING);
+ goto out;
+ }
+ if (atomic_cmpset_acq_int(&nfsrv_dnsnamelen, 0, 0) == 0) {
+ /*
+ * Free up all the old stuff and reinitialize hash
+ * lists. All mutexes for both lists must be locked,
+ * with the user/group name ones before the uid/gid
+ * ones, to avoid a LOR.
+ */
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_lock(&nfsusernamehash[i].mtx);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_lock(&nfsuserhash[i].mtx);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ TAILQ_FOREACH_SAFE(usrp,
+ &nfsuserhash[i].lughead, lug_numhash, nusrp)
+ nfsrv_removeuser(usrp, 1);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_unlock(&nfsuserhash[i].mtx);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_unlock(&nfsusernamehash[i].mtx);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_lock(&nfsgroupnamehash[i].mtx);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_lock(&nfsgrouphash[i].mtx);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ TAILQ_FOREACH_SAFE(usrp,
+ &nfsgrouphash[i].lughead, lug_numhash,
+ nusrp)
+ nfsrv_removeuser(usrp, 0);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_unlock(&nfsgrouphash[i].mtx);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_unlock(&nfsgroupnamehash[i].mtx);
+ free(nfsrv_dnsname, M_NFSSTRING);
+ nfsrv_dnsname = NULL;
+ }
+ if (nfsuserhash == NULL) {
+ /* Allocate the hash tables. */
+ nfsuserhash = malloc(sizeof(struct nfsrv_lughash) *
+ nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
+ M_ZERO);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_init(&nfsuserhash[i].mtx, "nfsuidhash",
+ NULL, MTX_DEF | MTX_DUPOK);
+ nfsusernamehash = malloc(sizeof(struct nfsrv_lughash) *
+ nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
+ M_ZERO);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_init(&nfsusernamehash[i].mtx,
+ "nfsusrhash", NULL, MTX_DEF |
+ MTX_DUPOK);
+ nfsgrouphash = malloc(sizeof(struct nfsrv_lughash) *
+ nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
+ M_ZERO);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_init(&nfsgrouphash[i].mtx, "nfsgidhash",
+ NULL, MTX_DEF | MTX_DUPOK);
+ nfsgroupnamehash = malloc(sizeof(struct nfsrv_lughash) *
+ nfsrv_lughashsize, M_NFSUSERGROUP, M_WAITOK |
+ M_ZERO);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_init(&nfsgroupnamehash[i].mtx,
+ "nfsgrphash", NULL, MTX_DEF | MTX_DUPOK);
+ }
+ /* (Re)initialize the list heads. */
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ TAILQ_INIT(&nfsuserhash[i].lughead);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ TAILQ_INIT(&nfsusernamehash[i].lughead);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ TAILQ_INIT(&nfsgrouphash[i].lughead);
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ TAILQ_INIT(&nfsgroupnamehash[i].lughead);
+
/*
- * Free up all the old stuff and reinitialize hash lists.
+ * Put name in "DNS" string.
*/
- TAILQ_FOREACH_SAFE(usrp, &nfsuserlruhead, lug_lru, nusrp) {
- nfsrv_removeuser(usrp);
- }
- free(nfsrv_dnsname, M_NFSSTRING);
- nfsrv_dnsname = NULL;
- }
- TAILQ_INIT(&nfsuserlruhead);
- for (i = 0; i < NFSUSERHASHSIZE; i++)
- LIST_INIT(&nfsuserhash[i]);
- for (i = 0; i < NFSGROUPHASHSIZE; i++)
- LIST_INIT(&nfsgrouphash[i]);
- for (i = 0; i < NFSUSERHASHSIZE; i++)
- LIST_INIT(&nfsusernamehash[i]);
- for (i = 0; i < NFSGROUPHASHSIZE; i++)
- LIST_INIT(&nfsgroupnamehash[i]);
-
- /*
- * Put name in "DNS" string.
- */
- if (!error) {
nfsrv_dnsname = cp;
- nfsrv_dnsnamelen = nidp->nid_namelen;
nfsrv_defaultuid = nidp->nid_uid;
nfsrv_defaultgid = nidp->nid_gid;
nfsrv_usercnt = 0;
nfsrv_usermax = nidp->nid_usermax;
- }
- NFSUNLOCKNAMEID();
- if (error)
- free(cp, M_NFSSTRING);
- goto out;
+ atomic_store_rel_int(&nfsrv_dnsnamelen, nidp->nid_namelen);
+ goto out;
}
/*
* malloc the new one now, so any potential sleep occurs before
* manipulation of the lists.
*/
- MALLOC(newusrp, struct nfsusrgrp *, sizeof (struct nfsusrgrp) +
- nidp->nid_namelen, M_NFSUSERGROUP, M_WAITOK);
+ newusrp = malloc(sizeof(struct nfsusrgrp) + nidp->nid_namelen,
+ M_NFSUSERGROUP, M_WAITOK | M_ZERO);
error = copyin(CAST_USER_ADDR_T(nidp->nid_name), newusrp->lug_name,
nidp->nid_namelen);
+ if (error == 0 && nidp->nid_ngroup > 0 &&
+ (nidp->nid_flag & NFSID_ADDUID) != 0) {
+ grps = malloc(sizeof(gid_t) * nidp->nid_ngroup, M_TEMP,
+ M_WAITOK);
+ error = copyin(CAST_USER_ADDR_T(nidp->nid_grps), grps,
+ sizeof(gid_t) * nidp->nid_ngroup);
+ if (error == 0) {
+ /*
+ * Create a credential just like svc_getcred(),
+ * but using the group list provided.
+ */
+ cr = crget();
+ cr->cr_uid = cr->cr_ruid = cr->cr_svuid = nidp->nid_uid;
+ crsetgroups(cr, nidp->nid_ngroup, grps);
+ cr->cr_rgid = cr->cr_svgid = cr->cr_groups[0];
+ cr->cr_prison = &prison0;
+ prison_hold(cr->cr_prison);
+#ifdef MAC
+ mac_cred_associate_nfsd(cr);
+#endif
+ newusrp->lug_cred = cr;
+ }
+ free(grps, M_TEMP);
+ }
if (error) {
- free((caddr_t)newusrp, M_NFSUSERGROUP);
+ free(newusrp, M_NFSUSERGROUP);
goto out;
}
newusrp->lug_namelen = nidp->nid_namelen;
- NFSLOCKNAMEID();
+ /*
+ * The lock order is username[0]->[nfsrv_lughashsize - 1] followed
+ * by uid[0]->[nfsrv_lughashsize - 1], with the same for group.
+ * The flags user_locked, username_locked, group_locked and
+ * groupname_locked are set to indicate all of those hash lists are
+ * locked. hp_name != NULL and hp_idnum != NULL indicates that
+ * the respective one mutex is locked.
+ */
+ user_locked = username_locked = group_locked = groupname_locked = 0;
+ hp_name = hp_idnum = NULL;
+
/*
* Delete old entries, as required.
*/
if (nidp->nid_flag & (NFSID_DELUID | NFSID_ADDUID)) {
- hp = NFSUSERHASH(nidp->nid_uid);
- LIST_FOREACH_SAFE(usrp, hp, lug_numhash, nusrp) {
+ /* Must lock all username hash lists first, to avoid a LOR. */
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_lock(&nfsusernamehash[i].mtx);
+ username_locked = 1;
+ hp_idnum = NFSUSERHASH(nidp->nid_uid);
+ mtx_lock(&hp_idnum->mtx);
+ TAILQ_FOREACH_SAFE(usrp, &hp_idnum->lughead, lug_numhash,
+ nusrp) {
if (usrp->lug_uid == nidp->nid_uid)
- nfsrv_removeuser(usrp);
+ nfsrv_removeuser(usrp, 1);
}
- }
- if (nidp->nid_flag & (NFSID_DELUSERNAME | NFSID_ADDUSERNAME)) {
- hp = NFSUSERNAMEHASH(newusrp->lug_name, newusrp->lug_namelen);
- LIST_FOREACH_SAFE(usrp, hp, lug_namehash, nusrp) {
+ } else if (nidp->nid_flag & (NFSID_DELUSERNAME | NFSID_ADDUSERNAME)) {
+ hp_name = NFSUSERNAMEHASH(newusrp->lug_name,
+ newusrp->lug_namelen);
+ mtx_lock(&hp_name->mtx);
+ TAILQ_FOREACH_SAFE(usrp, &hp_name->lughead, lug_namehash,
+ nusrp) {
if (usrp->lug_namelen == newusrp->lug_namelen &&
!NFSBCMP(usrp->lug_name, newusrp->lug_name,
- usrp->lug_namelen))
- nfsrv_removeuser(usrp);
+ usrp->lug_namelen)) {
+ thp = NFSUSERHASH(usrp->lug_uid);
+ mtx_lock(&thp->mtx);
+ nfsrv_removeuser(usrp, 1);
+ mtx_unlock(&thp->mtx);
+ }
}
- }
- if (nidp->nid_flag & (NFSID_DELGID | NFSID_ADDGID)) {
- hp = NFSGROUPHASH(nidp->nid_gid);
- LIST_FOREACH_SAFE(usrp, hp, lug_numhash, nusrp) {
+ hp_idnum = NFSUSERHASH(nidp->nid_uid);
+ mtx_lock(&hp_idnum->mtx);
+ } else if (nidp->nid_flag & (NFSID_DELGID | NFSID_ADDGID)) {
+ /* Must lock all groupname hash lists first, to avoid a LOR. */
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_lock(&nfsgroupnamehash[i].mtx);
+ groupname_locked = 1;
+ hp_idnum = NFSGROUPHASH(nidp->nid_gid);
+ mtx_lock(&hp_idnum->mtx);
+ TAILQ_FOREACH_SAFE(usrp, &hp_idnum->lughead, lug_numhash,
+ nusrp) {
if (usrp->lug_gid == nidp->nid_gid)
- nfsrv_removeuser(usrp);
+ nfsrv_removeuser(usrp, 0);
}
- }
- if (nidp->nid_flag & (NFSID_DELGROUPNAME | NFSID_ADDGROUPNAME)) {
- hp = NFSGROUPNAMEHASH(newusrp->lug_name, newusrp->lug_namelen);
- LIST_FOREACH_SAFE(usrp, hp, lug_namehash, nusrp) {
+ } else if (nidp->nid_flag & (NFSID_DELGROUPNAME | NFSID_ADDGROUPNAME)) {
+ hp_name = NFSGROUPNAMEHASH(newusrp->lug_name,
+ newusrp->lug_namelen);
+ mtx_lock(&hp_name->mtx);
+ TAILQ_FOREACH_SAFE(usrp, &hp_name->lughead, lug_namehash,
+ nusrp) {
if (usrp->lug_namelen == newusrp->lug_namelen &&
!NFSBCMP(usrp->lug_name, newusrp->lug_name,
- usrp->lug_namelen))
- nfsrv_removeuser(usrp);
+ usrp->lug_namelen)) {
+ thp = NFSGROUPHASH(usrp->lug_gid);
+ mtx_lock(&thp->mtx);
+ nfsrv_removeuser(usrp, 0);
+ mtx_unlock(&thp->mtx);
+ }
}
- }
- TAILQ_FOREACH_SAFE(usrp, &nfsuserlruhead, lug_lru, nusrp) {
- if (usrp->lug_expiry < NFSD_MONOSEC)
- nfsrv_removeuser(usrp);
- }
- while (nfsrv_usercnt >= nfsrv_usermax) {
- usrp = TAILQ_FIRST(&nfsuserlruhead);
- nfsrv_removeuser(usrp);
+ hp_idnum = NFSGROUPHASH(nidp->nid_gid);
+ mtx_lock(&hp_idnum->mtx);
}
/*
@@ -3210,23 +3381,129 @@ nfssvc_idname(struct nfsd_idargs *nidp)
newusrp->lug_expiry = NFSD_MONOSEC + 5;
if (nidp->nid_flag & (NFSID_ADDUID | NFSID_ADDUSERNAME)) {
newusrp->lug_uid = nidp->nid_uid;
- LIST_INSERT_HEAD(NFSUSERHASH(newusrp->lug_uid), newusrp,
- lug_numhash);
- LIST_INSERT_HEAD(NFSUSERNAMEHASH(newusrp->lug_name,
- newusrp->lug_namelen), newusrp, lug_namehash);
- TAILQ_INSERT_TAIL(&nfsuserlruhead, newusrp, lug_lru);
- nfsrv_usercnt++;
+ thp = NFSUSERHASH(newusrp->lug_uid);
+ mtx_assert(&thp->mtx, MA_OWNED);
+ TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_numhash);
+ thp = NFSUSERNAMEHASH(newusrp->lug_name, newusrp->lug_namelen);
+ mtx_assert(&thp->mtx, MA_OWNED);
+ TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_namehash);
+ atomic_add_int(&nfsrv_usercnt, 1);
} else if (nidp->nid_flag & (NFSID_ADDGID | NFSID_ADDGROUPNAME)) {
newusrp->lug_gid = nidp->nid_gid;
- LIST_INSERT_HEAD(NFSGROUPHASH(newusrp->lug_gid), newusrp,
- lug_numhash);
- LIST_INSERT_HEAD(NFSGROUPNAMEHASH(newusrp->lug_name,
- newusrp->lug_namelen), newusrp, lug_namehash);
- TAILQ_INSERT_TAIL(&nfsuserlruhead, newusrp, lug_lru);
- nfsrv_usercnt++;
- } else
- FREE((caddr_t)newusrp, M_NFSUSERGROUP);
- NFSUNLOCKNAMEID();
+ thp = NFSGROUPHASH(newusrp->lug_gid);
+ mtx_assert(&thp->mtx, MA_OWNED);
+ TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_numhash);
+ thp = NFSGROUPNAMEHASH(newusrp->lug_name, newusrp->lug_namelen);
+ mtx_assert(&thp->mtx, MA_OWNED);
+ TAILQ_INSERT_TAIL(&thp->lughead, newusrp, lug_namehash);
+ atomic_add_int(&nfsrv_usercnt, 1);
+ } else {
+ if (newusrp->lug_cred != NULL)
+ crfree(newusrp->lug_cred);
+ free(newusrp, M_NFSUSERGROUP);
+ }
+
+ /*
+ * Once per second, allow one thread to trim the cache.
+ */
+ if (lasttime < NFSD_MONOSEC &&
+ atomic_cmpset_acq_int(&onethread, 0, 1) != 0) {
+ /*
+ * First, unlock the single mutexes, so that all entries
+ * can be locked and any LOR is avoided.
+ */
+ if (hp_name != NULL) {
+ mtx_unlock(&hp_name->mtx);
+ hp_name = NULL;
+ }
+ if (hp_idnum != NULL) {
+ mtx_unlock(&hp_idnum->mtx);
+ hp_idnum = NULL;
+ }
+
+ if ((nidp->nid_flag & (NFSID_DELUID | NFSID_ADDUID |
+ NFSID_DELUSERNAME | NFSID_ADDUSERNAME)) != 0) {
+ if (username_locked == 0) {
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_lock(&nfsusernamehash[i].mtx);
+ username_locked = 1;
+ }
+ KASSERT(user_locked == 0,
+ ("nfssvc_idname: user_locked"));
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_lock(&nfsuserhash[i].mtx);
+ user_locked = 1;
+ for (i = 0; i < nfsrv_lughashsize; i++) {
+ TAILQ_FOREACH_SAFE(usrp,
+ &nfsuserhash[i].lughead, lug_numhash,
+ nusrp)
+ if (usrp->lug_expiry < NFSD_MONOSEC)
+ nfsrv_removeuser(usrp, 1);
+ }
+ for (i = 0; i < nfsrv_lughashsize; i++) {
+ /*
+ * Trim the cache using an approximate LRU
+ * algorithm. This code deletes the least
+ * recently used entry on each hash list.
+ */
+ if (nfsrv_usercnt <= nfsrv_usermax)
+ break;
+ usrp = TAILQ_FIRST(&nfsuserhash[i].lughead);
+ if (usrp != NULL)
+ nfsrv_removeuser(usrp, 1);
+ }
+ } else {
+ if (groupname_locked == 0) {
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_lock(&nfsgroupnamehash[i].mtx);
+ groupname_locked = 1;
+ }
+ KASSERT(group_locked == 0,
+ ("nfssvc_idname: group_locked"));
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_lock(&nfsgrouphash[i].mtx);
+ group_locked = 1;
+ for (i = 0; i < nfsrv_lughashsize; i++) {
+ TAILQ_FOREACH_SAFE(usrp,
+ &nfsgrouphash[i].lughead, lug_numhash,
+ nusrp)
+ if (usrp->lug_expiry < NFSD_MONOSEC)
+ nfsrv_removeuser(usrp, 0);
+ }
+ for (i = 0; i < nfsrv_lughashsize; i++) {
+ /*
+ * Trim the cache using an approximate LRU
+ * algorithm. This code deletes the least
+ * recently user entry on each hash list.
+ */
+ if (nfsrv_usercnt <= nfsrv_usermax)
+ break;
+ usrp = TAILQ_FIRST(&nfsgrouphash[i].lughead);
+ if (usrp != NULL)
+ nfsrv_removeuser(usrp, 0);
+ }
+ }
+ lasttime = NFSD_MONOSEC;
+ atomic_store_rel_int(&onethread, 0);
+ }
+
+ /* Now, unlock all locked mutexes. */
+ if (hp_idnum != NULL)
+ mtx_unlock(&hp_idnum->mtx);
+ if (hp_name != NULL)
+ mtx_unlock(&hp_name->mtx);
+ if (user_locked != 0)
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_unlock(&nfsuserhash[i].mtx);
+ if (username_locked != 0)
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_unlock(&nfsusernamehash[i].mtx);
+ if (group_locked != 0)
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_unlock(&nfsgrouphash[i].mtx);
+ if (groupname_locked != 0)
+ for (i = 0; i < nfsrv_lughashsize; i++)
+ mtx_unlock(&nfsgroupnamehash[i].mtx);
out:
NFSEXITCODE(error);
return (error);
@@ -3236,15 +3513,29 @@ out:
* Remove a user/group name element.
*/
static void
-nfsrv_removeuser(struct nfsusrgrp *usrp)
+nfsrv_removeuser(struct nfsusrgrp *usrp, int isuser)
{
-
- NFSNAMEIDREQUIRED();
- LIST_REMOVE(usrp, lug_numhash);
- LIST_REMOVE(usrp, lug_namehash);
- TAILQ_REMOVE(&nfsuserlruhead, usrp, lug_lru);
- nfsrv_usercnt--;
- FREE((caddr_t)usrp, M_NFSUSERGROUP);
+ struct nfsrv_lughash *hp;
+
+ if (isuser != 0) {
+ hp = NFSUSERHASH(usrp->lug_uid);
+ mtx_assert(&hp->mtx, MA_OWNED);
+ TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
+ hp = NFSUSERNAMEHASH(usrp->lug_name, usrp->lug_namelen);
+ mtx_assert(&hp->mtx, MA_OWNED);
+ TAILQ_REMOVE(&hp->lughead, usrp, lug_namehash);
+ } else {
+ hp = NFSGROUPHASH(usrp->lug_gid);
+ mtx_assert(&hp->mtx, MA_OWNED);
+ TAILQ_REMOVE(&hp->lughead, usrp, lug_numhash);
+ hp = NFSGROUPNAMEHASH(usrp->lug_name, usrp->lug_namelen);
+ mtx_assert(&hp->mtx, MA_OWNED);
+ TAILQ_REMOVE(&hp->lughead, usrp, lug_namehash);
+ }
+ atomic_add_int(&nfsrv_usercnt, -1);
+ if (usrp->lug_cred != NULL)
+ crfree(usrp->lug_cred);
+ free(usrp, M_NFSUSERGROUP);
}
/*
OpenPOWER on IntegriCloud