Remove uidinfo hash table lookup and maintenance out of chgproccnt() and

chgsbsize(), which are called rather frequently and may be called from an
interrupt context in the case of chgsbsize().  Instead, do the hash table
lookup and maintenance when credentials are changed, which is a lot less
frequent.  Add pointers to the uidinfo structures to the ucred and pcred
structures for fast access.  Pass a pointer to the credential to chgproccnt()
and chgsbsize() instead of passing the uid.  Add a reference count to the
uidinfo structure and use it to decide when to free the structure rather
than freeing the structure when the resource consumption drops to zero.
Move the resource tracking code from kern_proc.c to kern_resource.c.  Move
some duplicate code sequences in kern_prot.c to separate helper functions.
Change KASSERTs in this code to unconditional tests and calls to panic().
This commit is contained in:
Don Lewis 2000-09-05 22:11:13 +00:00
parent 68c9803b7f
commit f535380cb6
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=65495
15 changed files with 258 additions and 170 deletions

View File

@ -302,9 +302,11 @@ proc0_init(void *dummy __unused)
/* Create credentials. */
cred0.p_refcnt = 1;
cred0.p_uidinfo = uifind(0);
p->p_cred = &cred0;
p->p_ucred = crget();
p->p_ucred->cr_ngroups = 1; /* group 0 */
p->p_ucred->cr_uidinfo = uifind(0);
/* Don't jail it */
p->p_prison = 0;
@ -360,7 +362,7 @@ proc0_init(void *dummy __unused)
/*
* Charge root for one process.
*/
(void)chgproccnt(0, 1, 0);
(void)chgproccnt(cred0.p_uidinfo, 1, 0);
/*
* Initialize the current process pointer (curproc) before

View File

@ -43,6 +43,7 @@
#include <sys/pioctl.h>
#include <sys/malloc.h>
#include <sys/namei.h>
#include <sys/resourcevar.h>
#include <sys/sysent.h>
#include <sys/shm.h>
#include <sys/sysctl.h>
@ -294,7 +295,7 @@ interpret:
*/
p->p_ucred = crcopy(p->p_ucred);
if (attr.va_mode & VSUID)
p->p_ucred->cr_uid = attr.va_uid;
change_euid(p, attr.va_uid);
if (attr.va_mode & VSGID)
p->p_ucred->cr_gid = attr.va_gid;
setsugid(p);

View File

@ -472,7 +472,7 @@ loop:
/*
* Decrement the count of procs running with this uid.
*/
(void)chgproccnt(p->p_cred->p_ruid, -1, 0);
(void)chgproccnt(p->p_cred->p_uidinfo, -1, 0);
/*
* Release reference to text vnode
@ -485,6 +485,7 @@ loop:
*/
if (--p->p_cred->p_refcnt == 0) {
crfree(p->p_ucred);
uifree(p->p_cred->p_uidinfo);
FREE(p->p_cred, M_SUBPROC);
p->p_cred = NULL;
}

View File

@ -246,7 +246,8 @@ fork1(p1, flags, procp)
* Increment the count of procs running with this uid. Don't allow
* a nonprivileged user to exceed their current limit.
*/
ok = chgproccnt(uid, 1, p1->p_rlimit[RLIMIT_NPROC].rlim_cur);
ok = chgproccnt(p1->p_cred->p_uidinfo, 1,
p1->p_rlimit[RLIMIT_NPROC].rlim_cur);
if (uid != 0 && !ok) {
/*
* Back out the process count
@ -359,6 +360,7 @@ again:
bcopy(p1->p_cred, p2->p_cred, sizeof(*p2->p_cred));
p2->p_cred->p_refcnt = 1;
crhold(p1->p_ucred);
uihold(p1->p_cred->p_uidinfo);
if (p2->p_prison) {
p2->p_prison->pr_ref++;

View File

@ -61,25 +61,8 @@ SYSCTL_INT(_kern, OID_AUTO, ps_showallprocs, CTLFLAG_RW,
static void pgdelete __P((struct pgrp *));
/*
* Structure associated with user cacheing.
*/
struct uidinfo {
LIST_ENTRY(uidinfo) ui_hash;
uid_t ui_uid;
long ui_proccnt;
rlim_t ui_sbsize;
};
#define UIHASH(uid) (&uihashtbl[(uid) & uihash])
static LIST_HEAD(uihashhead, uidinfo) *uihashtbl;
static u_long uihash; /* size of hash table - 1 */
static void orphanpg __P((struct pgrp *pg));
static struct uidinfo *uifind(uid_t uid);
static struct uidinfo *uicreate(uid_t uid);
static int uifree(struct uidinfo *uip);
/*
* Other process lists
*/
@ -102,126 +85,8 @@ procinit()
LIST_INIT(&zombproc);
pidhashtbl = hashinit(maxproc / 4, M_PROC, &pidhash);
pgrphashtbl = hashinit(maxproc / 4, M_PROC, &pgrphash);
uihashtbl = hashinit(maxproc / 16, M_PROC, &uihash);
proc_zone = zinit("PROC", sizeof (struct proc), 0, 0, 5);
}
/*
* find/create a uidinfo struct for the uid passed in
*/
static struct uidinfo *
uifind(uid)
uid_t uid;
{
struct uihashhead *uipp;
struct uidinfo *uip;
uipp = UIHASH(uid);
LIST_FOREACH(uip, uipp, ui_hash)
if (uip->ui_uid == uid)
break;
return (uip);
}
static struct uidinfo *
uicreate(uid)
uid_t uid;
{
struct uidinfo *uip, *norace;
MALLOC(uip, struct uidinfo *, sizeof(*uip), M_PROC, M_NOWAIT);
if (uip == NULL) {
MALLOC(uip, struct uidinfo *, sizeof(*uip), M_PROC, M_WAITOK);
/*
* if we M_WAITOK we must look afterwards or risk
* redundant entries
*/
norace = uifind(uid);
if (norace != NULL) {
FREE(uip, M_PROC);
return (norace);
}
}
LIST_INSERT_HEAD(UIHASH(uid), uip, ui_hash);
uip->ui_uid = uid;
uip->ui_proccnt = 0;
uip->ui_sbsize = 0;
return (uip);
}
static int
uifree(uip)
struct uidinfo *uip;
{
if (uip->ui_sbsize == 0 && uip->ui_proccnt == 0) {
LIST_REMOVE(uip, ui_hash);
FREE(uip, M_PROC);
return (1);
}
return (0);
}
/*
* Change the count associated with number of processes
* a given user is using. When 'max' is 0, don't enforce a limit
*/
int
chgproccnt(uid, diff, max)
uid_t uid;
int diff;
int max;
{
struct uidinfo *uip;
uip = uifind(uid);
if (diff < 0)
KASSERT(uip != NULL, ("reducing proccnt: lost count, uid = %d", uid));
if (uip == NULL)
uip = uicreate(uid);
/* don't allow them to exceed max, but allow subtraction */
if (diff > 0 && uip->ui_proccnt + diff > max && max != 0) {
(void)uifree(uip);
return (0);
}
uip->ui_proccnt += diff;
(void)uifree(uip);
return (1);
}
/*
* Change the total socket buffer size a user has used.
*/
int
chgsbsize(uid, hiwat, to, max)
uid_t uid;
u_long *hiwat;
u_long to;
rlim_t max;
{
struct uidinfo *uip;
rlim_t diff;
int s;
uip = uifind(uid);
KASSERT(to >= *hiwat || uip != NULL,
("reducing sbsize: lost count, uid = %d", uid));
if (uip == NULL)
uip = uicreate(uid);
s = splnet();
diff = (rlim_t)to - (rlim_t)*hiwat;
/* don't allow them to exceed max, but allow subtraction */
if (diff > 0 && uip->ui_sbsize + diff > max) {
(void)uifree(uip);
splx(s);
return (0);
}
uip->ui_sbsize += diff;
*hiwat = to;
(void)uifree(uip);
splx(s);
return (1);
uihashinit();
}
/*

View File

@ -53,10 +53,13 @@
#include <sys/proc.h>
#include <sys/malloc.h>
#include <sys/pioctl.h>
#include <sys/resourcevar.h>
#include <sys/sysctl.h>
static MALLOC_DEFINE(M_CRED, "cred", "credentials");
static void change_ruid(struct proc *p, uid_t ruid);
#ifndef _SYS_SYSPROTO_H_
struct getpid_args {
int dummy;
@ -427,18 +430,16 @@ setuid(p, uap)
#endif
{
/*
* Transfer proc count to new user.
* Set the real uid and transfer proc count to new user.
*/
if (uid != pc->p_ruid) {
(void)chgproccnt(pc->p_ruid, -1, 0);
(void)chgproccnt(uid, 1, 0);
change_ruid(p, uid);
setsugid(p);
}
/*
* Set real uid
*/
if (uid != pc->p_ruid) {
pc->p_ruid = uid;
setsugid(p);
}
/*
* Set saved uid
@ -458,8 +459,7 @@ setuid(p, uap)
* Copy credentials so other references do not see our changes.
*/
if (pc->pc_ucred->cr_uid != uid) {
pc->pc_ucred = crcopy(pc->pc_ucred);
pc->pc_ucred->cr_uid = uid;
change_euid(p, uid);
setsugid(p);
}
return (0);
@ -490,8 +490,7 @@ seteuid(p, uap)
* not see our changes.
*/
if (pc->pc_ucred->cr_uid != euid) {
pc->pc_ucred = crcopy(pc->pc_ucred);
pc->pc_ucred->cr_uid = euid;
change_euid(p, euid);
setsugid(p);
}
return (0);
@ -674,14 +673,11 @@ setreuid(p, uap)
return (error);
if (euid != (uid_t)-1 && pc->pc_ucred->cr_uid != euid) {
pc->pc_ucred = crcopy(pc->pc_ucred);
pc->pc_ucred->cr_uid = euid;
change_euid(p, euid);
setsugid(p);
}
if (ruid != (uid_t)-1 && pc->p_ruid != ruid) {
(void)chgproccnt(pc->p_ruid, -1, 0);
(void)chgproccnt(ruid, 1, 0);
pc->p_ruid = ruid;
change_ruid(p, ruid);
setsugid(p);
}
if ((ruid != (uid_t)-1 || pc->pc_ucred->cr_uid != pc->p_ruid) &&
@ -767,14 +763,11 @@ setresuid(p, uap)
(error = suser_xxx(0, p, PRISON_ROOT)) != 0)
return (error);
if (euid != (uid_t)-1 && pc->pc_ucred->cr_uid != euid) {
pc->pc_ucred = crcopy(pc->pc_ucred);
pc->pc_ucred->cr_uid = euid;
change_euid(p, euid);
setsugid(p);
}
if (ruid != (uid_t)-1 && pc->p_ruid != ruid) {
(void)chgproccnt(pc->p_ruid, -1, 0);
(void)chgproccnt(ruid, 1, 0);
pc->p_ruid = ruid;
change_ruid(p, ruid);
setsugid(p);
}
if (suid != (uid_t)-1 && pc->p_svuid != suid) {
@ -1157,8 +1150,16 @@ void
crfree(cr)
struct ucred *cr;
{
if (--cr->cr_ref == 0)
if (--cr->cr_ref == 0) {
/*
* Some callers of crget(), such as nfs_statfs(),
* allocate a temporary credential, but don't
* allocate a uidinfo structure.
*/
if (cr->cr_uidinfo != NULL)
uifree(cr->cr_uidinfo);
FREE((caddr_t)cr, M_CRED);
}
}
/*
@ -1174,6 +1175,7 @@ crcopy(cr)
return (cr);
newcr = crget();
*newcr = *cr;
uihold(newcr->cr_uidinfo);
crfree(cr);
newcr->cr_ref = 1;
return (newcr);
@ -1190,6 +1192,7 @@ crdup(cr)
newcr = crget();
*newcr = *cr;
uihold(newcr->cr_uidinfo);
newcr->cr_ref = 1;
return (newcr);
}
@ -1253,3 +1256,50 @@ setsugid(p)
if (!(p->p_pfsflags & PF_ISUGID))
p->p_stops = 0;
}
/*
* Helper function to change the effective uid of a process
*/
void
change_euid(p, euid)
struct proc *p;
uid_t euid;
{
struct pcred *pc;
struct uidinfo *uip;
pc = p->p_cred;
/*
* crcopy is essentially a NOP if ucred has a reference count
* of 1, which is true if it has already been copied.
*/
pc->pc_ucred = crcopy(pc->pc_ucred);
uip = pc->pc_ucred->cr_uidinfo;
pc->pc_ucred->cr_uid = euid;
pc->pc_ucred->cr_uidinfo = uifind(euid);
uifree(uip);
}
/*
* Helper function to change the real uid of a process
*
* The per-uid process count for this process is transfered from
* the old uid to the new uid.
*/
static void
change_ruid(p, ruid)
struct proc *p;
uid_t ruid;
{
struct pcred *pc;
struct uidinfo *uip;
pc = p->p_cred;
(void)chgproccnt(pc->p_uidinfo, -1, 0);
uip = pc->p_uidinfo;
/* It is assumed that pcred is not shared between processes */
pc->p_ruid = ruid;
pc->p_uidinfo = uifind(ruid);
(void)chgproccnt(pc->p_uidinfo, 1, 0);
uifree(uip);
}

View File

@ -46,6 +46,7 @@
#include <sys/systm.h>
#include <sys/sysproto.h>
#include <sys/file.h>
#include <sys/kernel.h>
#include <sys/resourcevar.h>
#include <sys/malloc.h>
#include <sys/proc.h>
@ -61,6 +62,14 @@ static int donice __P((struct proc *curp, struct proc *chgp, int n));
/* dosetrlimit non-static: Needed by SysVR4 emulator */
int dosetrlimit __P((struct proc *p, u_int which, struct rlimit *limp));
static MALLOC_DEFINE(M_UIDINFO, "uidinfo", "uidinfo structures");
#define UIHASH(uid) (&uihashtbl[(uid) & uihash])
static LIST_HEAD(uihashhead, uidinfo) *uihashtbl;
static u_long uihash; /* size of hash table - 1 */
static struct uidinfo *uicreate __P((uid_t uid));
static struct uidinfo *uilookup __P((uid_t uid));
/*
* Resource controls and accounting.
*/
@ -641,3 +650,136 @@ limcopy(lim)
copy->p_refcnt = 1;
return (copy);
}
/*
* Find the uidinfo structure for a uid. This structure is used to
* track the total resource consumption (process count, socket buffer
* size, etc.) for the uid and impose limits.
*/
void
uihashinit()
{
uihashtbl = hashinit(maxproc / 16, M_UIDINFO, &uihash);
}
static struct uidinfo *
uilookup(uid)
uid_t uid;
{
struct uihashhead *uipp;
struct uidinfo *uip;
uipp = UIHASH(uid);
LIST_FOREACH(uip, uipp, ui_hash)
if (uip->ui_uid == uid)
break;
return (uip);
}
static struct uidinfo *
uicreate(uid)
uid_t uid;
{
struct uidinfo *uip, *norace;
MALLOC(uip, struct uidinfo *, sizeof(*uip), M_UIDINFO, M_NOWAIT);
if (uip == NULL) {
MALLOC(uip, struct uidinfo *, sizeof(*uip), M_UIDINFO, M_WAITOK);
/*
* if we M_WAITOK we must look afterwards or risk
* redundant entries
*/
norace = uilookup(uid);
if (norace != NULL) {
FREE(uip, M_UIDINFO);
return (norace);
}
}
LIST_INSERT_HEAD(UIHASH(uid), uip, ui_hash);
uip->ui_uid = uid;
uip->ui_proccnt = 0;
uip->ui_sbsize = 0;
uip->ui_ref = 0;
return (uip);
}
struct uidinfo *
uifind(uid)
uid_t uid;
{
struct uidinfo *uip;
uip = uilookup(uid);
if (uip == NULL)
uip = uicreate(uid);
uip->ui_ref++;
return (uip);
}
int
uifree(uip)
struct uidinfo *uip;
{
if (--uip->ui_ref == 0) {
if (uip->ui_sbsize != 0)
/* XXX no %qd in kernel. Truncate. */
panic("freeing uidinfo: uid = %d, sbsize = %ld",
uip->ui_uid, (long)uip->ui_sbsize);
if (uip->ui_proccnt != 0)
panic("freeing uidinfo: uid = %d, proccnt = %ld",
uip->ui_uid, uip->ui_proccnt);
LIST_REMOVE(uip, ui_hash);
FREE(uip, M_UIDINFO);
return (1);
}
return (0);
}
/*
* Change the count associated with number of processes
* a given user is using. When 'max' is 0, don't enforce a limit
*/
int
chgproccnt(uip, diff, max)
struct uidinfo *uip;
int diff;
int max;
{
/* don't allow them to exceed max, but allow subtraction */
if (diff > 0 && uip->ui_proccnt + diff > max && max != 0)
return (0);
uip->ui_proccnt += diff;
if (uip->ui_proccnt < 0)
panic("negative proccnt for uid = %d", uip->ui_uid);
return (1);
}
/*
* Change the total socket buffer size a user has used.
*/
int
chgsbsize(uip, hiwat, to, max)
struct uidinfo *uip;
u_long *hiwat;
u_long to;
rlim_t max;
{
rlim_t new;
int s;
s = splnet();
new = uip->ui_sbsize + to - *hiwat;
/* don't allow them to exceed max, but allow subtraction */
if (to > *hiwat && new > max) {
splx(s);
return (0);
}
uip->ui_sbsize = new;
*hiwat = to;
if (uip->ui_sbsize < 0)
panic("negative sbsize for uid = %d", uip->ui_uid);
splx(s);
return (1);
}

View File

@ -427,7 +427,7 @@ sbreserve(sb, cc, so, p)
*/
if ((u_quad_t)cc > (u_quad_t)sb_max * MCLBYTES / (MSIZE + MCLBYTES))
return (0);
if (!chgsbsize(so->so_cred->cr_uid, &sb->sb_hiwat, cc,
if (!chgsbsize(so->so_cred->cr_uidinfo, &sb->sb_hiwat, cc,
p ? p->p_rlimit[RLIMIT_SBSIZE].rlim_cur : RLIM_INFINITY)) {
return (0);
}
@ -447,7 +447,8 @@ sbrelease(sb, so)
{
sbflush(sb);
(void)chgsbsize(so->so_cred->cr_uid, &sb->sb_hiwat, 0, RLIM_INFINITY);
(void)chgsbsize(so->so_cred->cr_uidinfo, &sb->sb_hiwat, 0,
RLIM_INFINITY);
sb->sb_mbmax = 0;
}

View File

@ -190,10 +190,10 @@ sodealloc(so)
so->so_gencnt = ++so_gencnt;
if (so->so_rcv.sb_hiwat)
(void)chgsbsize(so->so_cred->cr_uid,
(void)chgsbsize(so->so_cred->cr_uidinfo,
&so->so_rcv.sb_hiwat, 0, RLIM_INFINITY);
if (so->so_snd.sb_hiwat)
(void)chgsbsize(so->so_cred->cr_uid,
(void)chgsbsize(so->so_cred->cr_uidinfo,
&so->so_snd.sb_hiwat, 0, RLIM_INFINITY);
if (so->so_accf != NULL) {
if (so->so_accf->so_accept_filter != NULL &&

View File

@ -427,7 +427,7 @@ sbreserve(sb, cc, so, p)
*/
if ((u_quad_t)cc > (u_quad_t)sb_max * MCLBYTES / (MSIZE + MCLBYTES))
return (0);
if (!chgsbsize(so->so_cred->cr_uid, &sb->sb_hiwat, cc,
if (!chgsbsize(so->so_cred->cr_uidinfo, &sb->sb_hiwat, cc,
p ? p->p_rlimit[RLIMIT_SBSIZE].rlim_cur : RLIM_INFINITY)) {
return (0);
}
@ -447,7 +447,8 @@ sbrelease(sb, so)
{
sbflush(sb);
(void)chgsbsize(so->so_cred->cr_uid, &sb->sb_hiwat, 0, RLIM_INFINITY);
(void)chgsbsize(so->so_cred->cr_uidinfo, &sb->sb_hiwat, 0,
RLIM_INFINITY);
sb->sb_mbmax = 0;
}

View File

@ -238,7 +238,7 @@ uipc_rcvd(struct socket *so, int flags)
unp->unp_mbcnt = so->so_rcv.sb_mbcnt;
newhiwat = so2->so_snd.sb_hiwat + unp->unp_cc -
so->so_rcv.sb_cc;
(void)chgsbsize(so2->so_cred->cr_uid, &so2->so_snd.sb_hiwat,
(void)chgsbsize(so2->so_cred->cr_uidinfo, &so2->so_snd.sb_hiwat,
newhiwat, RLIM_INFINITY);
unp->unp_cc = so->so_rcv.sb_cc;
sowwakeup(so2);
@ -347,7 +347,7 @@ uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
unp->unp_conn->unp_mbcnt = so2->so_rcv.sb_mbcnt;
newhiwat = so->so_snd.sb_hiwat -
(so2->so_rcv.sb_cc - unp->unp_conn->unp_cc);
(void)chgsbsize(so->so_cred->cr_uid, &so->so_snd.sb_hiwat,
(void)chgsbsize(so->so_cred->cr_uidinfo, &so->so_snd.sb_hiwat,
newhiwat, RLIM_INFINITY);
unp->unp_conn->unp_cc = so2->so_rcv.sb_cc;
sorwakeup(so2);

View File

@ -673,6 +673,8 @@ aio_daemon(void *uproc)
mycp->p_fd = NULL;
mycp->p_ucred = crcopy(mycp->p_ucred);
mycp->p_ucred->cr_uid = 0;
uifree(mycp->p_ucred->cr_uidinfo);
mycp->p_ucred->cr_uidinfo = uifind(0);
mycp->p_ucred->cr_ngroups = 1;
mycp->p_ucred->cr_groups[0] = 1;

View File

@ -312,6 +312,7 @@ struct pcred {
gid_t p_rgid; /* Real group id. */
gid_t p_svgid; /* Saved effective group id. */
int p_refcnt; /* Number of references. */
struct uidinfo *p_uidinfo; /* Per uid resource consumption */
};
@ -427,8 +428,6 @@ struct pgrp *pgfind __P((pid_t)); /* Find process group by id. */
struct vm_zone;
extern struct vm_zone *proc_zone;
int chgproccnt __P((uid_t uid, int diff, int max));
int chgsbsize __P((uid_t uid, u_long *hiwat, u_long to, rlim_t max));
int enterpgrp __P((struct proc *p, pid_t pgid, int mksess));
void fixjobc __P((struct proc *p, struct pgrp *pgrp, int entering));
int inferior __P((struct proc *p));

View File

@ -38,6 +38,7 @@
#define _SYS_RESOURCEVAR_H_
#include <sys/resource.h>
#include <sys/queue.h>
/*
* Kernel per-process accounting / statistics
@ -80,18 +81,37 @@ struct plimit {
rlim_t p_cpulimit; /* current cpu limit in usec */
};
/*
* Per uid resource consumption
*/
struct uidinfo {
LIST_ENTRY(uidinfo) ui_hash;
rlim_t ui_sbsize; /* socket buffer space consumed */
long ui_proccnt; /* number of processes */
uid_t ui_uid; /* uid */
u_short ui_ref; /* reference count */
};
#ifdef _KERNEL
#define uihold(uip) (uip)->ui_ref++
struct proc;
void addupc_intr __P((struct proc *p, u_long pc, u_int ticks));
void addupc_task __P((struct proc *p, u_long pc, u_int ticks));
void calcru __P((struct proc *p, struct timeval *up, struct timeval *sp,
struct timeval *ip));
int chgproccnt __P((struct uidinfo *uip, int diff, int max));
int chgsbsize __P((struct uidinfo *uip, u_long *hiwat, u_long to,
rlim_t max));
int fuswintr __P((void *base));
struct plimit
*limcopy __P((struct plimit *lim));
void ruadd __P((struct rusage *ru, struct rusage *ru2));
int suswintr __P((void *base, int word));
struct uidinfo
*uifind __P((uid_t uid));
int uifree __P((struct uidinfo *uip));
void uihashinit __P((void));
#endif
#endif /* !_SYS_RESOURCEVAR_H_ */

View File

@ -48,6 +48,7 @@ struct ucred {
uid_t cr_uid; /* effective user id */
short cr_ngroups; /* number of groups */
gid_t cr_groups[NGROUPS]; /* groups */
struct uidinfo *cr_uidinfo; /* per uid resource consumption */
};
#define cr_gid cr_groups[0]
#define NOCRED ((struct ucred *)0) /* no credential available */
@ -56,6 +57,7 @@ struct ucred {
#ifdef _KERNEL
#define crhold(cr) (cr)->cr_ref++
void change_euid __P((struct proc *p, uid_t euid));
struct ucred *crcopy __P((struct ucred *cr));
struct ucred *crdup __P((struct ucred *cr));
void crfree __P((struct ucred *cr));