#include <sys/param.h>
#include <sys/errno.h>
#include <sys/disp.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <sys/cred.h>
#include <sys/cmn_err.h>
#include <sys/systm.h>
#include <sys/kmem.h>
#include <sys/pathname.h>
#include <sys/utsname.h>
#include <sys/debug.h>
#include <sys/sysmacros.h>
#include <sys/list.h>
#include <sys/sunddi.h>
#include <sys/dnlc.h>
#include <sys/sdt.h>
#include <sys/pkp_hash.h>
#include <nfs/nfs4.h>
#include <nfs/rnode4.h>
#include <nfs/nfsid_map.h>
#include <nfs/nfs4_idmap_impl.h>
#include <nfs/nfssys.h>
zone_key_t nfsidmap_zone_key;
static list_t nfsidmap_globals_list;
static kmutex_t nfsidmap_globals_lock;
static kmem_cache_t *nfsidmap_cache;
static int nfs4_idcache_tout;
#define MOD2(a, pow_of_2) ((a) & ((pow_of_2) - 1))
#define _CACHE_TOUT (60*60)
#define TIMEOUT(x) (gethrestime_sec() > \
((x) + nfs4_idcache_tout))
#define _MAXIDSTRLEN 11
#define ID_HASH(id, hash) \
{ \
(hash) = MOD2(((id) ^ NFSID_CACHE_ANCHORS), NFSID_CACHE_ANCHORS); \
}
static void *nfs_idmap_init_zone(zoneid_t);
static void nfs_idmap_fini_zone(zoneid_t, void *);
static int is_stringified_id(utf8string *);
static void nfs_idmap_i2s_literal(uid_t, utf8string *);
static int nfs_idmap_s2i_literal(utf8string *, uid_t *, int);
static void nfs_idmap_reclaim(void *);
static void nfs_idmap_cache_reclaim(idmap_cache_info_t *);
static void nfs_idmap_cache_create(idmap_cache_info_t *, const char *);
static void nfs_idmap_cache_destroy(idmap_cache_info_t *);
static void nfs_idmap_cache_flush(idmap_cache_info_t *);
static uint_t nfs_idmap_cache_s2i_lkup(idmap_cache_info_t *, utf8string *,
uint_t *, uid_t *);
static uint_t nfs_idmap_cache_i2s_lkup(idmap_cache_info_t *, uid_t,
uint_t *, utf8string *);
static void nfs_idmap_cache_s2i_insert(idmap_cache_info_t *, uid_t,
utf8string *, hash_stat, uint_t);
static void nfs_idmap_cache_i2s_insert(idmap_cache_info_t *, uid_t,
utf8string *, hash_stat, uint_t);
static void nfs_idmap_cache_rment(nfsidmap_t *);
void
nfs_idmap_init(void)
{
nfsidmap_cache = kmem_cache_create("NFS_idmap_cache",
sizeof (nfsidmap_t), 0, NULL, NULL, nfs_idmap_reclaim, NULL,
NULL, 0);
if (!nfs4_idcache_tout)
nfs4_idcache_tout = _CACHE_TOUT;
mutex_init(&nfsidmap_globals_lock, NULL, MUTEX_DEFAULT, NULL);
list_create(&nfsidmap_globals_list, sizeof (struct nfsidmap_globals),
offsetof(struct nfsidmap_globals, nig_link));
zone_key_create(&nfsidmap_zone_key, nfs_idmap_init_zone, NULL,
nfs_idmap_fini_zone);
}
void
nfs_idmap_fini(void)
{
(void) zone_key_delete(nfsidmap_zone_key);
list_destroy(&nfsidmap_globals_list);
mutex_destroy(&nfsidmap_globals_lock);
kmem_cache_destroy(nfsidmap_cache);
}
static void *
nfs_idmap_init_zone(zoneid_t zoneid)
{
struct nfsidmap_globals *nig;
nig = kmem_alloc(sizeof (*nig), KM_SLEEP);
nig->nig_msg_done = 0;
mutex_init(&nig->nfsidmap_daemon_lock, NULL, MUTEX_DEFAULT, NULL);
nig->nfsidmap_pid = NOPID;
nig->nfsidmap_daemon_dh = NULL;
nfs_idmap_cache_create(&nig->u2s_ci, "u2s_cache");
nig->u2s_ci.nfsidmap_daemon_dh = &nig->nfsidmap_daemon_dh;
nfs_idmap_cache_create(&nig->s2u_ci, "s2u_cache");
nig->s2u_ci.nfsidmap_daemon_dh = &nig->nfsidmap_daemon_dh;
nfs_idmap_cache_create(&nig->g2s_ci, "g2s_cache");
nig->g2s_ci.nfsidmap_daemon_dh = &nig->nfsidmap_daemon_dh;
nfs_idmap_cache_create(&nig->s2g_ci, "s2g_cache");
nig->s2g_ci.nfsidmap_daemon_dh = &nig->nfsidmap_daemon_dh;
mutex_enter(&nfsidmap_globals_lock);
list_insert_head(&nfsidmap_globals_list, nig);
mutex_exit(&nfsidmap_globals_lock);
return (nig);
}
static void
nfs_idmap_fini_zone(zoneid_t zoneid, void *arg)
{
struct nfsidmap_globals *nig = arg;
mutex_enter(&nfsidmap_globals_lock);
list_remove(&nfsidmap_globals_list, nig);
nfs_idmap_cache_destroy(&nig->u2s_ci);
nfs_idmap_cache_destroy(&nig->s2u_ci);
nfs_idmap_cache_destroy(&nig->g2s_ci);
nfs_idmap_cache_destroy(&nig->s2g_ci);
mutex_exit(&nfsidmap_globals_lock);
if (nig->nfsidmap_daemon_dh)
door_ki_rele(nig->nfsidmap_daemon_dh);
mutex_destroy(&nig->nfsidmap_daemon_lock);
kmem_free(nig, sizeof (*nig));
}
int
nfs_idmap_str_uid(utf8string *u8s, uid_t *uid, bool_t isserver)
{
int error;
uint_t hashno = 0;
const char *whoami = "nfs_idmap_str_uid";
struct nfsidmap_globals *nig;
struct mapid_arg *mapargp;
struct mapid_res mapres;
struct mapid_res *mapresp = &mapres;
struct mapid_res *resp = mapresp;
door_arg_t door_args;
door_handle_t dh;
nig = zone_getspecific(nfsidmap_zone_key, nfs_zone());
ASSERT(nig != NULL);
if (!u8s || !u8s->utf8string_val || u8s->utf8string_len == 0 ||
(u8s->utf8string_val[0] == '\0')) {
*uid = UID_NOBODY;
return (isserver ? EINVAL : 0);
}
if (bcmp(u8s->utf8string_val, "nobody", 6) == 0) {
*uid = UID_NOBODY;
return (0);
}
retry:
mutex_enter(&nig->nfsidmap_daemon_lock);
dh = nig->nfsidmap_daemon_dh;
if (dh)
door_ki_hold(dh);
mutex_exit(&nig->nfsidmap_daemon_lock);
if (dh == NULL || nig->nfsidmap_pid == curproc->p_pid ||
(!utf8_strchr(u8s, '@') && !isserver)) {
if (dh)
door_ki_rele(dh);
error = nfs_idmap_s2i_literal(u8s, uid, isserver);
if (!error && nig->nfsidmap_pid == curproc->p_pid)
return (ENOTSUP);
return (error);
}
if (nfs_idmap_cache_s2i_lkup(&nig->s2u_ci, u8s, &hashno, uid)) {
door_ki_rele(dh);
return (0);
}
mapargp = kmem_alloc(MAPID_ARG_LEN(u8s->utf8string_len), KM_SLEEP);
mapargp->cmd = NFSMAPID_STR_UID;
mapargp->u_arg.len = u8s->utf8string_len;
(void) bcopy(u8s->utf8string_val, mapargp->str, mapargp->u_arg.len);
mapargp->str[mapargp->u_arg.len] = '\0';
door_args.data_ptr = (char *)mapargp;
door_args.data_size = MAPID_ARG_LEN(mapargp->u_arg.len);
door_args.desc_ptr = NULL;
door_args.desc_num = 0;
door_args.rbuf = (char *)mapresp;
door_args.rsize = sizeof (struct mapid_res);
error = door_ki_upcall_limited(dh, &door_args, NULL, SIZE_MAX, 0);
if (!error) {
resp = (struct mapid_res *)door_args.rbuf;
ASSERT(resp->status != NFSMAPID_INVALID);
switch (resp->status) {
case NFSMAPID_OK:
*uid = resp->u_res.uid;
nfs_idmap_cache_s2i_insert(&nig->s2u_ci, *uid,
u8s, HQ_HASH_HINT, hashno);
break;
case NFSMAPID_NUMSTR:
*uid = resp->u_res.uid;
break;
case NFSMAPID_BADDOMAIN:
DTRACE_PROBE1(nfs4__str__uid, char *, mapargp->str);
case NFSMAPID_INVALID:
case NFSMAPID_UNMAPPABLE:
case NFSMAPID_INTERNAL:
case NFSMAPID_BADID:
case NFSMAPID_NOTFOUND:
default:
if (isserver)
error = EPERM;
else
*uid = UID_NOBODY;
break;
}
kmem_free(mapargp, MAPID_ARG_LEN(u8s->utf8string_len));
if (resp != mapresp)
kmem_free(door_args.rbuf, door_args.rsize);
door_ki_rele(dh);
return (error);
}
kmem_free(mapargp, MAPID_ARG_LEN(u8s->utf8string_len));
switch (error) {
case EINTR:
if (ttolwp(curthread) && ISSIG(curthread, JUSTLOOKING)) {
door_ki_rele(dh);
return (EINTR);
}
case EAGAIN:
door_ki_rele(dh);
delay(hz);
goto retry;
default:
case EBADF:
case EINVAL:
mutex_enter(&nig->nfsidmap_daemon_lock);
if (dh == nig->nfsidmap_daemon_dh) {
door_ki_rele(nig->nfsidmap_daemon_dh);
nig->nfsidmap_daemon_dh = NULL;
}
mutex_exit(&nig->nfsidmap_daemon_lock);
door_ki_rele(dh);
if (isserver)
return (ECOMM);
if (!nig->nig_msg_done) {
zcmn_err(getzoneid(), CE_WARN,
"!%s: Can't communicate with mapping daemon "
"nfsmapid", whoami);
nig->nig_msg_done = 1;
}
*uid = UID_NOBODY;
return (0);
}
}
int
nfs_idmap_uid_str(uid_t uid, utf8string *u8s, bool_t isserver)
{
int error;
uint_t hashno = 0;
const char *whoami = "nfs_idmap_uid_str";
struct nfsidmap_globals *nig;
struct mapid_arg maparg;
struct mapid_res mapres;
struct mapid_res *mapresp = &mapres;
struct mapid_res *resp = mapresp;
door_arg_t door_args;
door_handle_t dh;
nig = zone_getspecific(nfsidmap_zone_key, nfs_zone());
ASSERT(nig != NULL);
if (uid == UID_NOBODY) {
(void) str_to_utf8("nobody", u8s);
return (0);
}
retry:
mutex_enter(&nig->nfsidmap_daemon_lock);
dh = nig->nfsidmap_daemon_dh;
if (dh)
door_ki_hold(dh);
mutex_exit(&nig->nfsidmap_daemon_lock);
if (dh == NULL || nig->nfsidmap_pid == curproc->p_pid) {
if (dh)
door_ki_rele(dh);
nfs_idmap_i2s_literal(uid, u8s);
return (0);
}
if (nfs_idmap_cache_i2s_lkup(&nig->u2s_ci, uid, &hashno, u8s)) {
door_ki_rele(dh);
return (0);
}
maparg.cmd = NFSMAPID_UID_STR;
maparg.u_arg.uid = uid;
door_args.data_ptr = (char *)&maparg;
door_args.data_size = sizeof (struct mapid_arg);
door_args.desc_ptr = NULL;
door_args.desc_num = 0;
door_args.rbuf = (char *)mapresp;
door_args.rsize = sizeof (struct mapid_res);
error = door_ki_upcall_limited(dh, &door_args, NULL, SIZE_MAX, 0);
if (!error) {
resp = (struct mapid_res *)door_args.rbuf;
ASSERT(resp->status != NFSMAPID_INVALID);
switch (resp->status) {
case NFSMAPID_OK:
(void) str_to_utf8(resp->str, u8s);
nfs_idmap_cache_i2s_insert(&nig->u2s_ci, uid,
u8s, HQ_HASH_HINT, hashno);
break;
case NFSMAPID_INVALID:
case NFSMAPID_UNMAPPABLE:
case NFSMAPID_INTERNAL:
case NFSMAPID_BADDOMAIN:
case NFSMAPID_BADID:
case NFSMAPID_NOTFOUND:
default:
error = EPERM;
break;
}
if (resp != mapresp)
kmem_free(door_args.rbuf, door_args.rsize);
door_ki_rele(dh);
return (error);
}
switch (error) {
case EINTR:
if (ttolwp(curthread) && ISSIG(curthread, JUSTLOOKING)) {
door_ki_rele(dh);
return (EINTR);
}
case EAGAIN:
door_ki_rele(dh);
delay(hz);
goto retry;
default:
case EBADF:
case EINVAL:
mutex_enter(&nig->nfsidmap_daemon_lock);
if (dh == nig->nfsidmap_daemon_dh) {
door_ki_rele(nig->nfsidmap_daemon_dh);
nig->nfsidmap_daemon_dh = NULL;
}
mutex_exit(&nig->nfsidmap_daemon_lock);
door_ki_rele(dh);
if (!nig->nig_msg_done && !isserver) {
zcmn_err(getzoneid(), CE_WARN,
"!%s: Can't communicate with mapping daemon "
"nfsmapid", whoami);
nig->nig_msg_done = 1;
}
nfs_idmap_i2s_literal(uid, u8s);
return (0);
}
}
int
nfs_idmap_str_gid(utf8string *u8s, gid_t *gid, bool_t isserver)
{
int error;
uint_t hashno = 0;
const char *whoami = "nfs_idmap_str_gid";
struct nfsidmap_globals *nig;
struct mapid_arg *mapargp;
struct mapid_res mapres;
struct mapid_res *mapresp = &mapres;
struct mapid_res *resp = mapresp;
door_arg_t door_args;
door_handle_t dh;
nig = zone_getspecific(nfsidmap_zone_key, nfs_zone());
ASSERT(nig != NULL);
if (!u8s || !u8s->utf8string_val || u8s->utf8string_len == 0 ||
(u8s->utf8string_val[0] == '\0')) {
*gid = GID_NOBODY;
return (isserver ? EINVAL : 0);
}
if (bcmp(u8s->utf8string_val, "nobody", 6) == 0) {
*gid = GID_NOBODY;
return (0);
}
retry:
mutex_enter(&nig->nfsidmap_daemon_lock);
dh = nig->nfsidmap_daemon_dh;
if (dh)
door_ki_hold(dh);
mutex_exit(&nig->nfsidmap_daemon_lock);
if (dh == NULL || nig->nfsidmap_pid == curproc->p_pid ||
(!utf8_strchr(u8s, '@') && !isserver)) {
if (dh)
door_ki_rele(dh);
error = nfs_idmap_s2i_literal(u8s, gid, isserver);
if (!error && nig->nfsidmap_pid == curproc->p_pid)
return (ENOTSUP);
return (error);
}
if (nfs_idmap_cache_s2i_lkup(&nig->s2g_ci, u8s, &hashno, gid)) {
door_ki_rele(dh);
return (0);
}
mapargp = kmem_alloc(MAPID_ARG_LEN(u8s->utf8string_len), KM_SLEEP);
mapargp->cmd = NFSMAPID_STR_GID;
mapargp->u_arg.len = u8s->utf8string_len;
(void) bcopy(u8s->utf8string_val, mapargp->str, mapargp->u_arg.len);
mapargp->str[mapargp->u_arg.len] = '\0';
door_args.data_ptr = (char *)mapargp;
door_args.data_size = MAPID_ARG_LEN(mapargp->u_arg.len);
door_args.desc_ptr = NULL;
door_args.desc_num = 0;
door_args.rbuf = (char *)mapresp;
door_args.rsize = sizeof (struct mapid_res);
error = door_ki_upcall_limited(dh, &door_args, NULL, SIZE_MAX, 0);
if (!error) {
resp = (struct mapid_res *)door_args.rbuf;
ASSERT(resp->status != NFSMAPID_INVALID);
switch (resp->status) {
case NFSMAPID_OK:
*gid = resp->u_res.gid;
error = 0;
nfs_idmap_cache_s2i_insert(&nig->s2g_ci, *gid,
u8s, HQ_HASH_HINT, hashno);
break;
case NFSMAPID_NUMSTR:
*gid = resp->u_res.gid;
break;
case NFSMAPID_BADDOMAIN:
DTRACE_PROBE1(nfs4__str__gid, char *, mapargp->str);
case NFSMAPID_INVALID:
case NFSMAPID_UNMAPPABLE:
case NFSMAPID_INTERNAL:
case NFSMAPID_BADID:
case NFSMAPID_NOTFOUND:
default:
if (isserver)
error = EPERM;
else
*gid = GID_NOBODY;
break;
}
kmem_free(mapargp, MAPID_ARG_LEN(u8s->utf8string_len));
if (resp != mapresp)
kmem_free(door_args.rbuf, door_args.rsize);
door_ki_rele(dh);
return (error);
}
kmem_free(mapargp, MAPID_ARG_LEN(u8s->utf8string_len));
switch (error) {
case EINTR:
if (ttolwp(curthread) && ISSIG(curthread, JUSTLOOKING)) {
door_ki_rele(dh);
return (EINTR);
}
case EAGAIN:
door_ki_rele(dh);
delay(hz);
goto retry;
default:
case EBADF:
case EINVAL:
mutex_enter(&nig->nfsidmap_daemon_lock);
if (dh == nig->nfsidmap_daemon_dh) {
door_ki_rele(nig->nfsidmap_daemon_dh);
nig->nfsidmap_daemon_dh = NULL;
}
mutex_exit(&nig->nfsidmap_daemon_lock);
door_ki_rele(dh);
if (isserver)
return (ECOMM);
if (!nig->nig_msg_done) {
zcmn_err(getzoneid(), CE_WARN,
"!%s: Can't communicate with mapping daemon "
"nfsmapid", whoami);
nig->nig_msg_done = 1;
}
*gid = GID_NOBODY;
return (0);
}
}
int
nfs_idmap_gid_str(gid_t gid, utf8string *u8s, bool_t isserver)
{
int error;
uint_t hashno = 0;
const char *whoami = "nfs_idmap_gid_str";
struct nfsidmap_globals *nig;
struct mapid_arg maparg;
struct mapid_res mapres;
struct mapid_res *mapresp = &mapres;
struct mapid_res *resp = mapresp;
door_arg_t door_args;
door_handle_t dh;
nig = zone_getspecific(nfsidmap_zone_key, nfs_zone());
ASSERT(nig != NULL);
if (gid == GID_NOBODY) {
(void) str_to_utf8("nobody", u8s);
return (0);
}
retry:
mutex_enter(&nig->nfsidmap_daemon_lock);
dh = nig->nfsidmap_daemon_dh;
if (dh)
door_ki_hold(dh);
mutex_exit(&nig->nfsidmap_daemon_lock);
if (dh == NULL || nig->nfsidmap_pid == curproc->p_pid) {
if (dh)
door_ki_rele(dh);
nfs_idmap_i2s_literal(gid, u8s);
return (0);
}
if (nfs_idmap_cache_i2s_lkup(&nig->g2s_ci, gid, &hashno, u8s)) {
door_ki_rele(dh);
return (0);
}
maparg.cmd = NFSMAPID_GID_STR;
maparg.u_arg.gid = gid;
door_args.data_ptr = (char *)&maparg;
door_args.data_size = sizeof (struct mapid_arg);
door_args.desc_ptr = NULL;
door_args.desc_num = 0;
door_args.rbuf = (char *)mapresp;
door_args.rsize = sizeof (struct mapid_res);
error = door_ki_upcall_limited(dh, &door_args, NULL, SIZE_MAX, 0);
if (!error) {
resp = (struct mapid_res *)door_args.rbuf;
ASSERT(resp->status != NFSMAPID_INVALID);
switch (resp->status) {
case NFSMAPID_OK:
(void) str_to_utf8(resp->str, u8s);
nfs_idmap_cache_i2s_insert(&nig->g2s_ci, gid,
u8s, HQ_HASH_HINT, hashno);
break;
case NFSMAPID_INVALID:
case NFSMAPID_UNMAPPABLE:
case NFSMAPID_INTERNAL:
case NFSMAPID_BADDOMAIN:
case NFSMAPID_BADID:
case NFSMAPID_NOTFOUND:
default:
error = EPERM;
break;
}
if (resp != mapresp)
kmem_free(door_args.rbuf, door_args.rsize);
door_ki_rele(dh);
return (error);
}
switch (error) {
case EINTR:
if (ttolwp(curthread) && ISSIG(curthread, JUSTLOOKING)) {
door_ki_rele(dh);
return (EINTR);
}
case EAGAIN:
door_ki_rele(dh);
delay(hz);
goto retry;
default:
case EBADF:
case EINVAL:
mutex_enter(&nig->nfsidmap_daemon_lock);
if (dh == nig->nfsidmap_daemon_dh) {
door_ki_rele(nig->nfsidmap_daemon_dh);
nig->nfsidmap_daemon_dh = NULL;
}
door_ki_rele(dh);
mutex_exit(&nig->nfsidmap_daemon_lock);
if (!nig->nig_msg_done && !isserver) {
zcmn_err(getzoneid(), CE_WARN,
"!%s: Can't communicate with mapping daemon "
"nfsmapid", whoami);
nig->nig_msg_done = 1;
}
nfs_idmap_i2s_literal(gid, u8s);
return (0);
}
}
static void
nfs_idmap_cache_create(idmap_cache_info_t *cip, const char *name)
{
int i;
nfsidhq_t *hq = NULL;
cip->table = kmem_alloc((NFSID_CACHE_ANCHORS * sizeof (nfsidhq_t)),
KM_SLEEP);
for (i = 0, hq = cip->table; i < NFSID_CACHE_ANCHORS; i++, hq++) {
hq->hq_que_forw = hq;
hq->hq_que_back = hq;
mutex_init(&(hq->hq_lock), NULL, MUTEX_DEFAULT, NULL);
}
cip->name = name;
}
static void
nfs_idmap_cache_destroy(idmap_cache_info_t *cip)
{
int i;
nfsidhq_t *hq;
ASSERT(MUTEX_HELD(&nfsidmap_globals_lock));
nfs_idmap_cache_flush(cip);
for (i = 0, hq = cip->table; i < NFSID_CACHE_ANCHORS; i++, hq++)
mutex_destroy(&(hq->hq_lock));
kmem_free(cip->table, NFSID_CACHE_ANCHORS * sizeof (nfsidhq_t));
}
void
nfs_idmap_args(struct nfsidmap_args *idmp)
{
struct nfsidmap_globals *nig;
nig = zone_getspecific(nfsidmap_zone_key, nfs_zone());
ASSERT(nig != NULL);
nfs_idmap_cache_flush(&nig->u2s_ci);
nfs_idmap_cache_flush(&nig->s2u_ci);
nfs_idmap_cache_flush(&nig->g2s_ci);
nfs_idmap_cache_flush(&nig->s2g_ci);
if (idmp->state) {
dnlc_purge();
nfs4_rnode_invalidate(NULL);
mutex_enter(&nig->nfsidmap_daemon_lock);
if (nig->nfsidmap_daemon_dh)
door_ki_rele(nig->nfsidmap_daemon_dh);
nig->nfsidmap_daemon_dh = door_ki_lookup(idmp->did);
nig->nfsidmap_pid = curproc->p_pid;
nig->nig_msg_done = 0;
mutex_exit(&nig->nfsidmap_daemon_lock);
}
}
static void
nfs_idmap_cache_flush(idmap_cache_info_t *cip)
{
int i;
nfsidmap_t *p, *next;
nfsidhq_t *hq;
for (i = 0, hq = cip->table; i < NFSID_CACHE_ANCHORS; i++, hq++) {
mutex_enter(&(hq->hq_lock));
p = hq->hq_lru_forw;
hq->hq_que_forw = hq;
hq->hq_que_back = hq;
mutex_exit(&(hq->hq_lock));
while (p != (nfsidmap_t *)hq) {
next = p->id_forw;
if (p->id_val != 0)
kmem_free(p->id_val, p->id_len);
kmem_cache_free(nfsidmap_cache, p);
p = next;
}
}
}
static void
nfs_idmap_cache_reclaim(idmap_cache_info_t *cip)
{
nfsidhq_t *hq;
nfsidmap_t *pprev = NULL;
int i;
nfsidmap_t *p;
ASSERT(cip != NULL && cip->table != NULL);
if ((*cip->nfsidmap_daemon_dh) == NULL)
return;
for (i = 0, hq = cip->table; i < NFSID_CACHE_ANCHORS; i++, hq++) {
if (!mutex_tryenter(&(hq->hq_lock)))
continue;
for (p = hq->hq_lru_back; p != (nfsidmap_t *)hq; p = pprev) {
pprev = p->id_back;
if (!TIMEOUT(p->id_time))
break;
nfs_idmap_cache_rment(p);
}
mutex_exit(&(hq->hq_lock));
}
}
void
nfs_idmap_reclaim(void *arg)
{
struct nfsidmap_globals *nig;
mutex_enter(&nfsidmap_globals_lock);
for (nig = list_head(&nfsidmap_globals_list); nig != NULL;
nig = list_next(&nfsidmap_globals_list, nig)) {
nfs_idmap_cache_reclaim(&nig->u2s_ci);
nfs_idmap_cache_reclaim(&nig->s2u_ci);
nfs_idmap_cache_reclaim(&nig->g2s_ci);
nfs_idmap_cache_reclaim(&nig->s2g_ci);
}
mutex_exit(&nfsidmap_globals_lock);
}
static uint_t
nfs_idmap_cache_s2i_lkup(idmap_cache_info_t *cip, utf8string *u8s,
uint_t *hashno, uid_t *id_buf)
{
nfsidmap_t *p;
nfsidmap_t *pnext;
nfsidhq_t *hq;
char *rqst_c_str;
uint_t rqst_len;
uint_t found_stat = 0;
if ((rqst_c_str = utf8_to_str(u8s, &rqst_len, NULL)) == NULL) {
return (0);
}
*hashno = pkp_tab_hash(rqst_c_str, rqst_len - 1);
hq = &cip->table[*hashno];
mutex_enter(&(hq->hq_lock));
for (p = hq->hq_lru_forw; p != (nfsidmap_t *)hq; p = pnext) {
pnext = p->id_forw;
if (TIMEOUT(p->id_time) && (*cip->nfsidmap_daemon_dh) != NULL) {
nfs_idmap_cache_rment(p);
continue;
}
if (p->id_len == (rqst_len - 1)) {
if (bcmp(p->id_val, rqst_c_str, (rqst_len - 1)) == 0) {
*id_buf = p->id_no;
remque(p);
insque(p, hq);
p->id_time = gethrestime_sec();
found_stat = 1;
break;
}
}
}
mutex_exit(&(hq->hq_lock));
if (rqst_c_str != NULL)
kmem_free(rqst_c_str, rqst_len);
return (found_stat);
}
static void
nfs_idmap_cache_s2i_insert(idmap_cache_info_t *cip, uid_t id, utf8string *u8s,
hash_stat behavior, uint_t hash_number)
{
uint_t hashno;
char *c_str;
nfsidhq_t *hq;
nfsidmap_t *newp;
nfsidmap_t *p;
nfsidmap_t *pnext;
uint_t c_len;
if ((c_str = utf8_to_str(u8s, &c_len, NULL)) == NULL)
return;
switch (behavior) {
case HQ_HASH_HINT:
hashno = hash_number;
break;
case HQ_HASH_FIND:
default:
hashno = pkp_tab_hash(c_str, c_len - 1);
break;
}
hq = &cip->table[hashno];
mutex_enter(&(hq->hq_lock));
for (p = hq->hq_lru_forw; p != (nfsidmap_t *)hq; p = pnext) {
pnext = p->id_forw;
if (TIMEOUT(p->id_time) && (*cip->nfsidmap_daemon_dh) != NULL) {
nfs_idmap_cache_rment(p);
continue;
}
if (p->id_len == (c_len - 1)) {
if (bcmp(p->id_val, c_str, (c_len - 1)) == 0) {
remque(p);
insque(p, hq);
p->id_time = gethrestime_sec();
mutex_exit(&(hq->hq_lock));
kmem_free(c_str, c_len);
return;
}
}
}
newp = kmem_cache_alloc(nfsidmap_cache, KM_SLEEP);
newp->id_len = u8s->utf8string_len;
newp->id_val = kmem_alloc(u8s->utf8string_len, KM_SLEEP);
bcopy(u8s->utf8string_val, newp->id_val, u8s->utf8string_len);
newp->id_no = id;
newp->id_time = gethrestime_sec();
insque(newp, hq);
mutex_exit(&(hq->hq_lock));
kmem_free(c_str, c_len);
}
static uint_t
nfs_idmap_cache_i2s_lkup(idmap_cache_info_t *cip, uid_t id, uint_t *hashno,
utf8string *u8s)
{
uint_t found_stat = 0;
nfsidmap_t *p;
nfsidmap_t *pnext;
nfsidhq_t *hq;
uint_t hash;
ID_HASH(id, hash);
*hashno = hash;
hq = &cip->table[hash];
mutex_enter(&(hq->hq_lock));
for (p = hq->hq_lru_forw; p != (nfsidmap_t *)hq; p = pnext) {
pnext = p->id_forw;
if (TIMEOUT(p->id_time) && (*cip->nfsidmap_daemon_dh) != NULL) {
nfs_idmap_cache_rment(p);
continue;
}
if (p->id_no == id) {
ASSERT(u8s->utf8string_val == NULL);
u8s->utf8string_len = p->id_len;
u8s->utf8string_val = kmem_alloc(p->id_len, KM_SLEEP);
bcopy(p->id_val, u8s->utf8string_val, p->id_len);
remque(p);
insque(p, hq);
p->id_time = gethrestime_sec();
found_stat = 1;
break;
}
}
mutex_exit(&(hq->hq_lock));
return (found_stat);
}
static void
nfs_idmap_cache_i2s_insert(idmap_cache_info_t *cip, uid_t id, utf8string *u8s,
hash_stat behavior, uint_t hash_number)
{
uint_t hashno;
nfsidhq_t *hq;
nfsidmap_t *newp;
nfsidmap_t *pnext;
nfsidmap_t *p;
switch (behavior) {
case HQ_HASH_HINT:
hashno = hash_number;
break;
case HQ_HASH_FIND:
default:
ID_HASH(id, hashno);
break;
}
hq = &cip->table[hashno];
mutex_enter(&(hq->hq_lock));
for (p = hq->hq_lru_forw; p != (nfsidmap_t *)hq; p = pnext) {
pnext = p->id_forw;
if (TIMEOUT(p->id_time) && (*cip->nfsidmap_daemon_dh) != NULL) {
nfs_idmap_cache_rment(p);
continue;
}
if ((p->id_no == id) && (p->id_len == u8s->utf8string_len)) {
remque(p);
insque(p, hq);
p->id_time = gethrestime_sec();
mutex_exit(&(hq->hq_lock));
return;
}
}
newp = kmem_cache_alloc(nfsidmap_cache, KM_SLEEP);
newp->id_len = u8s->utf8string_len;
newp->id_val = kmem_alloc(u8s->utf8string_len, KM_SLEEP);
bcopy(u8s->utf8string_val, newp->id_val, u8s->utf8string_len);
newp->id_no = id;
newp->id_time = gethrestime_sec();
insque(newp, hq);
mutex_exit(&(hq->hq_lock));
}
static void
nfs_idmap_cache_rment(nfsidmap_t *p)
{
remque(p);
if (p->id_val != 0)
kmem_free(p->id_val, p->id_len);
kmem_cache_free(nfsidmap_cache, p);
}
#ifndef UID_MAX
#define UID_MAX 2147483647
#endif
#ifndef isdigit
#define isdigit(c) ((c) >= '0' && (c) <= '9')
#endif
static int
is_stringified_id(utf8string *u8s)
{
int i;
for (i = 0; i < u8s->utf8string_len; i++)
if (!isdigit(u8s->utf8string_val[i]))
return (0);
return (1);
}
int
nfs_idmap_s2i_literal(utf8string *u8s, uid_t *id, int isserver)
{
long tmp;
int convd;
char ids[_MAXIDSTRLEN];
*id = UID_NOBODY;
if (!is_stringified_id(u8s))
return (0);
if (u8s->utf8string_len >= _MAXIDSTRLEN)
return (isserver ? EPERM : 0);
bcopy(u8s->utf8string_val, ids, u8s->utf8string_len);
ids[u8s->utf8string_len] = '\0';
convd = ddi_strtol(ids, NULL, 10, &tmp);
if (convd == 0 && tmp >= 0 && tmp <= UID_MAX) {
*id = tmp;
return (0);
}
return (isserver ? EPERM : 0);
}
static void
nfs_idmap_i2s_literal(uid_t id, utf8string *u8s)
{
char ids[_MAXIDSTRLEN];
(void) snprintf(ids, _MAXIDSTRLEN, "%d", id);
(void) str_to_utf8(ids, u8s);
}
char *
utf8_strchr(utf8string *u8s, const char c)
{
int i;
char *u8p = u8s->utf8string_val;
int len = u8s->utf8string_len;
for (i = 0; i < len; i++)
if (u8p[i] == c)
return (&u8p[i]);
return (NULL);
}