750 lines
18 KiB
C
750 lines
18 KiB
C
/* @(#)auth.c 1.1 92/07/30 SMI */
|
|
|
|
/* Copyright (c) 1984 AT&T */
|
|
/* All Rights Reserved */
|
|
|
|
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
|
|
/* The copyright notice above does not evidence any */
|
|
/* actual or intended publication of such source code. */
|
|
|
|
/* #ident "@(#)kern-port:nudnix/auth.c 10.8" */
|
|
#include <sys/param.h>
|
|
#include <sys/types.h>
|
|
#include <rfs/sema.h>
|
|
#include <sys/sysmacros.h>
|
|
#include <sys/param.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/signal.h>
|
|
#include <sys/user.h>
|
|
#include <sys/file.h>
|
|
#include <sys/debug.h>
|
|
#include <sys/mount.h>
|
|
#include <sys/vnode.h>
|
|
#include <sys/kmem_alloc.h>
|
|
#include <rfs/rfs_misc.h>
|
|
#include <rfs/comm.h>
|
|
#include <rfs/nserve.h>
|
|
#include <rfs/cirmgr.h>
|
|
#include <rfs/idtab.h>
|
|
#include <rfs/rdebug.h>
|
|
|
|
#ifndef BUFSIZ
|
|
#define BUFSIZ 1024
|
|
#endif
|
|
#ifndef NULL
|
|
#define NULL 0
|
|
#endif
|
|
#ifndef TRUE
|
|
#define TRUE 1
|
|
#endif
|
|
#ifndef FALSE
|
|
#define FALSE 0
|
|
#endif
|
|
#undef HEADSIZE
|
|
#define HEADSIZE (sizeof (struct idhead)/sizeof (struct idtab))
|
|
#define CACHESIZE 5
|
|
#define NBPC 2048
|
|
#define MAXALLOC (NBPC - (2 * sizeof (union heap)))
|
|
#define MAXNAMES 100 /* maximum # of names for auth list */
|
|
#define MAX_MLINKS 2 /* max number of gdp links per machine */
|
|
#define H_BUSY 0x01
|
|
typedef long ALIGN;
|
|
#define isbusy(x) (((unsigned int) (x)) & H_BUSY)
|
|
#define mkbusy(x) (((ALIGN) (x)) | H_BUSY)
|
|
#define mkfree(x) (((ALIGN) (x)) & ~H_BUSY)
|
|
#define howbig(x) ((mkfree(x->h_next)?mkfree(x->h_next):\
|
|
Pavail) - ((char *)(x+1)-Heap))
|
|
#define ASIZE sizeof (ALIGN)
|
|
#define otop(o) ((union heap *) (Heap + (((unsigned)(o)) & ~H_BUSY)))
|
|
#define ptoo(p) ((ALIGN) ((char *)(p) - Heap))
|
|
#define bump(s) ((s) + (((s)%ASIZE)?ASIZE-((s)%ASIZE):0))
|
|
|
|
#define TRTABPRI PWAIT
|
|
|
|
/*
|
|
* check(tabptr, value, return) compares tabptr (c in macro) with value (v)
|
|
* and if there is a match sets return (r) to the remote value.
|
|
* It returns, 0 if there is a match, 1 if c is higher in the table
|
|
* than v, and -1 if c is lower in the table than v.
|
|
*/
|
|
#define UF 0xFFFF
|
|
#define check(c, v, r) \
|
|
((c->i_rem == v) ?\
|
|
((c->i_loc == UF) ? (r = (c + 1)->i_loc, 0) : (r = c->i_loc, 0)) :\
|
|
((c->i_rem < v) ?\
|
|
((c->i_loc == UF && (c + 1)->i_rem >= v) ?\
|
|
(r = (c + 1)->i_loc, 0) : -1) :\
|
|
(((c-1)->i_loc == UF && (c-1)->i_rem <= v)?\
|
|
(r = c->i_loc, 0) : 1)))
|
|
|
|
static char *Global[2] = {0, 0}; /* global default table ref */
|
|
static int Tabwlock = 0; /* Table write lock */
|
|
static int Tabrlock = 0; /* Table read lock */
|
|
static int Want_wr = 0; /* I want to write */
|
|
static int Want_rd = 0; /* I want to read */
|
|
static void wr_lock(); /* Lock for writers */
|
|
static void rd_lock(); /* Lock for readers */
|
|
static void wr_unlock(); /* Unlock for writers */
|
|
static void rd_unlock(); /* Unlock for readers */
|
|
char Domain[MAXDNAME+1]=""; /* domain name for this machine */
|
|
|
|
union heap {
|
|
ALIGN h_align; /* put in to assure alignment */
|
|
long h_next;
|
|
};
|
|
|
|
static char *Heap=NULL; /* actual location of heap */
|
|
static int Pavail=0; /* free bytes */
|
|
|
|
int
|
|
auth_init()
|
|
{
|
|
Heap = NULL;
|
|
Global[0] = NULL;
|
|
Global[1] = NULL;
|
|
}
|
|
/*
|
|
* allocate a block of size bytes from the heap.
|
|
*/
|
|
char *
|
|
h_alloc(size)
|
|
int size;
|
|
{
|
|
register union heap *hp;
|
|
union heap *thp;
|
|
char *cp;
|
|
int fsize;
|
|
|
|
DUPRINT2(DB_MNT_ADV, "h_alloc(0x%x)\n", size);
|
|
wr_lock();
|
|
/* first time */
|
|
if (Heap == NULL) {
|
|
Heap = rfheap;
|
|
Pavail = rfsize;
|
|
hp = (union heap *) Heap;
|
|
hp->h_next = 0;
|
|
}
|
|
size = bump(size); /* set size so next alloc is aligned */
|
|
fsize = size + sizeof (union heap);
|
|
if (fsize > MAXALLOC) {
|
|
DUPRINT1(DB_MNT_ADV, "h_alloc failed, ENOMEM (size too big)\n");
|
|
u.u_error = ENOMEM;
|
|
wr_unlock();
|
|
return (NULL);
|
|
}
|
|
hp = (union heap *) Heap;
|
|
do {
|
|
if (!isbusy(hp->h_next)) {
|
|
while (mkfree(hp->h_next) && !isbusy(otop(hp->h_next)->h_next))
|
|
hp->h_next = otop(hp->h_next)->h_next;
|
|
if (howbig(hp) >= fsize) {
|
|
cp = (char *)hp;
|
|
thp = (union heap *) (cp + fsize);
|
|
thp->h_next = hp->h_next;
|
|
hp->h_next = (long) mkbusy((char *) thp - Heap);
|
|
wr_unlock();
|
|
DUPRINT2(DB_MNT_ADV, "h_alloc 1 returns 0x%x\n", hp+1);
|
|
return ((char *)(hp+1));
|
|
}
|
|
if (howbig(hp) >= size) {
|
|
hp->h_next = (long) mkbusy(hp->h_next);
|
|
wr_unlock();
|
|
DUPRINT2(DB_MNT_ADV, "h_alloc 2 returns 0x%x\n", hp+1);
|
|
return ((char *)(hp+1));
|
|
}
|
|
}
|
|
hp = otop(hp->h_next);
|
|
} while (hp != (union heap *) Heap);
|
|
|
|
DUPRINT1(DB_MNT_ADV, "h_alloc failed, ENOMEM\n");
|
|
u.u_error = ENOMEM;
|
|
wr_unlock();
|
|
return (0);
|
|
}
|
|
void
|
|
h_free(offset)
|
|
char *offset;
|
|
{
|
|
union heap *hp = (union heap *) offset;
|
|
|
|
DUPRINT2(DB_MNT_ADV, "h_free(%x)\n", offset);
|
|
wr_lock();
|
|
hp--; /* go back to h_next */
|
|
ASSERT((char *)hp >= Heap || (char *)hp < Heap + Pavail);
|
|
ASSERT(isbusy(hp->h_next) && (hp->h_next > 0 && hp->h_next < Pavail+1));
|
|
hp->h_next = (long) mkfree(hp->h_next);
|
|
wr_unlock();
|
|
return;
|
|
}
|
|
/*
|
|
* this is called with an idmap that is in the gdp table.
|
|
* this function only frees the map if there are 1 or 0 references
|
|
* to the map in the gdp structure. This allows multiple pointers
|
|
* to the same map from the gdp structure.
|
|
*/
|
|
int
|
|
freeidmap(offset)
|
|
char *offset;
|
|
{
|
|
register struct gdp *gp;
|
|
register int gcnt=0;
|
|
|
|
for (gp = gdp; gp < &gdp[maxgdp]; gp++) {
|
|
if (gp->idmap[0] == offset)
|
|
gcnt++;
|
|
if (gp->idmap[1] == offset)
|
|
gcnt++;
|
|
}
|
|
if (gcnt <= 1)
|
|
h_free(offset);
|
|
}
|
|
/*
|
|
* This section contains functions to add and remove
|
|
* authorization information for client machines.
|
|
*
|
|
* The functions are:
|
|
*
|
|
* addalist(list)
|
|
* char **list;
|
|
*
|
|
* This function adds a list to the heap and returns a
|
|
* reference to the list.
|
|
* The list must be terminated by a NULL character.
|
|
* The reference is an integer and not a pointer, it cannot
|
|
* be directly used to reference a list. Addalist returns 0, and
|
|
* sets u.u_error if it fails. Addalist reads the list
|
|
* from user space.
|
|
*
|
|
* remalist(ref)
|
|
* int ref;
|
|
*
|
|
* Remalist removes the list referred to by ref. It
|
|
* will cause an assertion error if ref is invalid.
|
|
*
|
|
* checkalist(ref, name)
|
|
* int ref;
|
|
* char *name;
|
|
*
|
|
* This function returns TRUE (1) if name is in the
|
|
* referenced list and FALSE otherwise. If ref is
|
|
* invalid, an assertion may occur, or an undefined
|
|
* result.
|
|
*
|
|
*/
|
|
char *
|
|
addalist(list)
|
|
char **list;
|
|
{
|
|
register int i;
|
|
register int j;
|
|
register char *lptr;
|
|
char *ref = 0; /* allocation reference to return */
|
|
int size = 0; /* size of block to be allocated */
|
|
int count = 0;
|
|
int upath();
|
|
char *buffer;
|
|
char **alist;
|
|
int *sizelist;
|
|
int upath();
|
|
|
|
DUPRINT2(DB_MNT_ADV, "addalist: list=%x\n", list);
|
|
buffer = new_kmem_alloc(BUFSIZ, KMEM_SLEEP);
|
|
alist = (char **)new_kmem_alloc(MAXNAMES * sizeof (char *), KMEM_SLEEP);
|
|
sizelist = (int *)new_kmem_alloc(MAXNAMES * sizeof (int), KMEM_SLEEP);
|
|
for (i=0; i < MAXNAMES; i++) {
|
|
DUPRINT3(DB_MNT_ADV, "addalist: list[%d]=%x\n", i, list);
|
|
if ((int) (alist[i] = (char *) fuword((caddr_t) list++)) == -1) {
|
|
DUPRINT2(DB_MNT_ADV, "addalist: EFAULT on list[%d]\n", i);
|
|
u.u_error = EFAULT;
|
|
goto out;
|
|
}
|
|
if (alist[i] == 0) {
|
|
count = i;
|
|
break;
|
|
}
|
|
}
|
|
if (i == MAXNAMES) {
|
|
u.u_error = EINVAL;
|
|
goto out;
|
|
}
|
|
for (i = 0; i < count; i++) {
|
|
if ((j = upath(alist[i], buffer, BUFSIZ)) < 0) {
|
|
u.u_error = (j == -1)?EFAULT:EINVAL;
|
|
DUPRINT4(DB_MNT_ADV,
|
|
"addalist: upath failed on alist[%d]=%x, errno=%s\n",
|
|
i, alist[i], (j== -1)?"EFAULT":"EINVAL");
|
|
goto out;
|
|
}
|
|
DUPRINT3(DB_MNT_ADV, "addalist: name[%d]=%s\n", i, buffer);
|
|
sizelist[i] = ++j;
|
|
size += j;
|
|
}
|
|
size++;
|
|
size = bump(size);
|
|
if ((ref = h_alloc(size)) == NULL) {
|
|
u.u_error = ENOMEM;
|
|
goto out;
|
|
}
|
|
lptr = ref;
|
|
for (i = 0; i < count; i++) {
|
|
*lptr++ = (char) sizelist[i]-1;
|
|
if ((j = upath(alist[i], lptr, BUFSIZ)) < 0) {
|
|
h_free(ref);
|
|
u.u_error = (j == -1)?EFAULT:EINVAL;
|
|
DUPRINT4(DB_MNT_ADV,
|
|
"addalist: 2nd upath failed alist[%d]=%x, errno=%s\n",
|
|
i, alist[i], (j== -1)?"EFAULT":"EINVAL");
|
|
goto out;
|
|
}
|
|
lptr += j;
|
|
}
|
|
/* now fill up rest with nulls */
|
|
for (i = (lptr - ref); i < size; i++)
|
|
*lptr++ = '\0';
|
|
out:
|
|
kmem_free((caddr_t) buffer, BUFSIZ);
|
|
kmem_free((caddr_t) alist, MAXNAMES * sizeof (char *));
|
|
kmem_free((caddr_t) sizelist, MAXNAMES * sizeof (int));
|
|
return (ref);
|
|
}
|
|
|
|
void
|
|
remalist(ref)
|
|
char *ref;
|
|
{
|
|
h_free(ref);
|
|
}
|
|
/*
|
|
* checkalist compares an incoming name, which is assumed to be
|
|
* a full domain name, with the names in the list that ref refers
|
|
* to. The following conditions constitute a match:
|
|
*
|
|
* 1. complete match of full names.
|
|
* 2. complete match of domain part of name if list item ends
|
|
* with a SEPARATOR. e.g., if name = "a.b.c" and the list
|
|
* item = "a.b." there would be a match because a.b. implies
|
|
* a match with any name in the domain a.b.
|
|
* 3. the name has the same dompart as this machine, and the
|
|
* namepart of name exactly matches an item in the list.
|
|
*
|
|
* NOTE: this code parses the names from left to right, i.e.,
|
|
* domain.name.
|
|
*/
|
|
int
|
|
checkalist(ref, name)
|
|
char *ref;
|
|
char *name;
|
|
{
|
|
register char *lp;
|
|
register char *np;
|
|
register char *nlp=NULL;
|
|
char *sp;
|
|
char *npart=NULL; /* if != NULL, pointer to namepart of name */
|
|
DUPRINT3(DB_MNT_ADV, "checkalist: ref=%x, name=%s\n", ref, name);
|
|
rd_lock();
|
|
for (np=name; *np != '\0'; np++)
|
|
;
|
|
|
|
/* first check to see if name is in same domain as machine */
|
|
for (np = name, lp = Domain; *lp != '\0'; lp++, np++)
|
|
if (*np != *lp)
|
|
break;
|
|
|
|
/* if we match domain totally, and name is at a separator, */
|
|
/* we call it a match and call whatever is left the namepart */
|
|
if (*np == SEPARATOR && *lp == '\0') {
|
|
npart = np+1;
|
|
DUPRINT3(DB_MNT_ADV, "checkalist: name (%s) is local, npart =%s\n",
|
|
name, npart);
|
|
}
|
|
|
|
for (lp = ref; *lp > 0; lp = nlp) {
|
|
nlp = lp + (int) *lp + 1;
|
|
sp = ++lp; /* go past the count */
|
|
/* first check for full name match or domain match */
|
|
for (np = name; *np != '\0'; np++, lp++)
|
|
if (*np != *lp)
|
|
break;
|
|
if ((*np == '\0' && nlp == lp) ||
|
|
(*(np-1) == SEPARATOR && lp == nlp && *(lp-1) == SEPARATOR)) {
|
|
rd_unlock();
|
|
DUPRINT1(DB_MNT_ADV, "checkalist returns TRUE, full match\n");
|
|
return (TRUE);
|
|
}
|
|
/* now check for current domain match */
|
|
if (!npart)
|
|
continue;
|
|
for (np = npart, lp = sp; *np != '\0'; np++, lp++)
|
|
if (*np != *lp)
|
|
break;
|
|
|
|
if (*np == '\0' && lp == nlp) {
|
|
rd_unlock();
|
|
DUPRINT1(DB_MNT_ADV, "checkalist returns TRUE, local domain\n");
|
|
return (TRUE);
|
|
}
|
|
}
|
|
rd_unlock();
|
|
DUPRINT1(DB_MNT_ADV, "checkalist returns FALSE\n");
|
|
return (FALSE);
|
|
}
|
|
/*
|
|
* translation table system call (actually called through rfsys).
|
|
* The uid, gid translation tables in the kernel are locked
|
|
* whenever they are updated (e.g., from a user level) or
|
|
* accessed from the kernel (e.g., through the kernel routines
|
|
* interface). This is to prevent updating the table during an
|
|
* access operation.
|
|
*
|
|
* setidmap:
|
|
* finds the gdp structure for the named machine,
|
|
* and adds uid or gid maps to the heap for the machine.
|
|
*/
|
|
setidmap(name, flag, map)
|
|
char *name;
|
|
int flag;
|
|
struct idtab *map;
|
|
{
|
|
char sname[MAXDNAME+1];
|
|
struct gdp *g;
|
|
int gcnt = 0;
|
|
struct idtab header;
|
|
char *ref = NULL;
|
|
char **refp[MAX_MLINKS];
|
|
int i;
|
|
struct idtab *itp;
|
|
struct idtab iclear;
|
|
struct idhead *hp;
|
|
|
|
DUPRINT4(DB_RFSYS, "setidmap: uname=%s, flag=%d, map=%x\n",
|
|
name ? name : "", flag, map);
|
|
|
|
if (!suser())
|
|
return;
|
|
|
|
/* NULL name says to clear all tables */
|
|
if (!name) {
|
|
for (g = gdp; g < &gdp[maxgdp]; g++)
|
|
if (g->flag) {
|
|
if (g->idmap[UID_DEV]) {
|
|
freeidmap(g->idmap[UID_DEV]);
|
|
g->idmap[UID_DEV] = 0;
|
|
}
|
|
if (g->idmap[GID_DEV]) {
|
|
freeidmap(g->idmap[GID_DEV]);
|
|
g->idmap[GID_DEV] = 0;
|
|
}
|
|
}
|
|
if (Global[UID_DEV]) {
|
|
h_free(Global[UID_DEV]);
|
|
Global[UID_DEV] = 0;
|
|
}
|
|
if (Global[GID_DEV]) {
|
|
h_free(Global[GID_DEV]);
|
|
Global[GID_DEV] = 0;
|
|
}
|
|
return;
|
|
}
|
|
/* copy in the machine name and initial information */
|
|
switch (upath(name, sname, MAXDNAME)) {
|
|
case -2: /* too long */
|
|
case 0: /* too short */
|
|
u.u_error = EINVAL;
|
|
return;
|
|
case -1: /* bad user address */
|
|
u.u_error = EFAULT;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* if name is GLOBAL_CH, this is global name, else
|
|
* look for name in gdp structure.
|
|
*/
|
|
if (sname[0] == GLOBAL_CH) {
|
|
refp[0] = (flag==UID_MAP)? &(Global[UID_DEV]): &(Global[GID_DEV]);
|
|
gcnt = 1;
|
|
} else {
|
|
for (g = gdp; g < &gdp[maxgdp] && gcnt < MAX_MLINKS; g++)
|
|
if (strcmp(sname, g->token.t_uname) == 0)
|
|
refp[gcnt++] = (flag == UID_MAP)?
|
|
&(g->idmap[UID_DEV]):
|
|
&(g->idmap[GID_DEV]);
|
|
|
|
if (gcnt == 0) {
|
|
u.u_error = ENOENT;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* if there was a map, set it */
|
|
if (map) {
|
|
if (copyin((char *) map, (char *) &header, sizeof (struct idtab))) {
|
|
u.u_error = EFAULT;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* free as late as possible, to save current map if something goes wrong */
|
|
for (i=0; i < gcnt; i++)
|
|
if (*refp[i]) {
|
|
freeidmap(*refp[i]);
|
|
*refp[i] = NULL;
|
|
}
|
|
|
|
if (map) {
|
|
if ((ref = h_alloc((int) (sizeof (struct idtab) *
|
|
(header.i_tblsiz + HEADSIZE + CACHESIZE)))) == NULL) {
|
|
u.u_error = ENOMEM;
|
|
return;
|
|
}
|
|
rd_lock();
|
|
|
|
/* set up header */
|
|
hp = (struct idhead *) ref;
|
|
hp->i_default = header.i_defval;
|
|
hp->i_size = header.i_tblsiz;
|
|
hp->i_cend = HEADSIZE + CACHESIZE;
|
|
hp->i_next = HEADSIZE;
|
|
hp->i_tries = 0;
|
|
hp->i_hits = 0;
|
|
|
|
/* mark cache unused */
|
|
itp = (struct idtab *) ref;
|
|
iclear.i_rem = UF;
|
|
iclear.i_loc = UF;
|
|
for (i=HEADSIZE; i < HEADSIZE+CACHESIZE; i++)
|
|
itp[i] = iclear;
|
|
|
|
/* copy in the table */
|
|
if (copyin((char *) (map+1), (char *) (itp+HEADSIZE+CACHESIZE),
|
|
sizeof (struct idtab) * header.i_tblsiz)) {
|
|
rd_unlock(); /* must unlock before free */
|
|
h_free(ref);
|
|
u.u_error = EFAULT;
|
|
return;
|
|
}
|
|
rd_unlock();
|
|
}
|
|
|
|
for (i = 0; i < gcnt; i++)
|
|
*refp[i] = ref;
|
|
return;
|
|
}
|
|
/*
|
|
* glid returns a local id given a struct gdp pointer
|
|
* and remote id. The argument dev chooses whether
|
|
* the uid or gid table is searched.
|
|
*
|
|
* glid uses a binary search to improve the search time.
|
|
* This requires the table to be sorted. The table is assumed
|
|
* to be sorted in ascending order with remote id as the
|
|
* primary sort key.
|
|
*
|
|
*/
|
|
|
|
ushort
|
|
glid(dev, gp, rid) /* get local id, given sysid & rid */
|
|
int dev;
|
|
struct gdp *gp;
|
|
register ushort rid;
|
|
{
|
|
register struct idtab *low; /* invariant: points to record <= rid */
|
|
register struct idtab *high; /* invariant: points to record >= rid */
|
|
register struct idtab *middle; /* points to most recent record */
|
|
ushort size; /* size of table */
|
|
ushort defval=OTHERID; /* default value for local id */
|
|
ushort ret=OTHERID; /* return value */
|
|
struct idtab *idt=NULL;
|
|
struct idhead *hp;
|
|
|
|
DUPRINT4(DB_RFSYS, "glid: dev=%d, gp=%x, rid=%u\n", dev, gp, rid);
|
|
|
|
rd_lock();
|
|
|
|
if (gp && gp->idmap[dev])
|
|
idt = (struct idtab *) gp->idmap[dev];
|
|
else if (Global[dev])
|
|
idt = (struct idtab *) Global[dev];
|
|
else
|
|
goto glid_out; /* ret already has default value */
|
|
|
|
/* at this point should have a good table */
|
|
DUPRINT2(DB_RFSYS, "glid: found table=%x\n", idt);
|
|
|
|
hp = (struct idhead *) idt;
|
|
size = idt->i_tblsiz;
|
|
defval = idt->i_defval;
|
|
|
|
/* check sets ret if it finds a match, if it doesn't, use default */
|
|
ret = (defval)?defval:rid;
|
|
|
|
if (size == 0)
|
|
goto glid_out;
|
|
|
|
low = idt + hp->i_cend;
|
|
high = idt + size + hp->i_cend - 1;
|
|
|
|
if (rid < low->i_rem || rid > high->i_rem) /* out of range */
|
|
goto glid_out;
|
|
|
|
/* check the extremes of range */
|
|
if (check(low, rid, ret) == 0)
|
|
goto glid_out;
|
|
if (check(high, rid, ret) == 0)
|
|
goto glid_out;
|
|
|
|
while (high - low > 1) {
|
|
middle = (high-low)/2 + low;
|
|
switch (check(middle, rid, ret)) {
|
|
case -1: /* middle is < rid */
|
|
low = middle;
|
|
break;
|
|
case 1: /* middle is > rid */
|
|
high = middle;
|
|
break;
|
|
case 0: /* found it */
|
|
goto glid_out;
|
|
}
|
|
}
|
|
glid_out:
|
|
rd_unlock();
|
|
DUPRINT2(DB_RFSYS, "glid: returns %u\n", ret);
|
|
return (ret);
|
|
}
|
|
static void
|
|
rd_lock()
|
|
{
|
|
(void) splrf();
|
|
Want_rd++;
|
|
while (Tabrlock)
|
|
(void) sleep((caddr_t) &Tabrlock, TRTABPRI);
|
|
|
|
Tabwlock++; /* lock potential writers */
|
|
Want_rd--;
|
|
(void) spl0();
|
|
}
|
|
static void
|
|
rd_unlock()
|
|
{
|
|
if (--Tabwlock == 0 && Want_wr)
|
|
wakeup((caddr_t) &Tabwlock);
|
|
else if (Want_rd) /* This may not be necessary */
|
|
wakeup((caddr_t) &Tabrlock);
|
|
}
|
|
static void
|
|
wr_lock()
|
|
{
|
|
(void) splrf();
|
|
Want_wr++;
|
|
while (Tabwlock || Tabrlock)
|
|
(void) sleep((caddr_t) &Tabwlock, TRTABPRI);
|
|
|
|
Tabrlock++; /* lock potential readers & writers */
|
|
Want_wr--;
|
|
(void) spl0();
|
|
}
|
|
static void
|
|
wr_unlock()
|
|
{
|
|
Tabrlock--;
|
|
if (Want_wr)
|
|
wakeup((caddr_t) &Tabwlock);
|
|
else if (Want_rd)
|
|
wakeup((caddr_t) &Tabrlock);
|
|
}
|
|
ushort rglid();
|
|
|
|
stat_rmap(gdpp, sptr)
|
|
struct gdp *gdpp;
|
|
struct rfs_stat *sptr;
|
|
{
|
|
sptr->st_uid = rglid(UID_DEV, gdpp, (ushort) sptr->st_uid);
|
|
sptr->st_gid = rglid(GID_DEV, gdpp, (ushort) sptr->st_gid);
|
|
return;
|
|
}
|
|
ushort
|
|
rglid(dev, gp, rid) /* get local id, given sysid & rid */
|
|
int dev;
|
|
struct gdp *gp;
|
|
register ushort rid;
|
|
{
|
|
register struct idtab *low; /* invariant: points to record <= rid */
|
|
register struct idtab *high; /* invariant: points to record >= rid */
|
|
ushort size; /* size of table */
|
|
ushort defval=OTHERID; /* default value for local id */
|
|
ushort ret=OTHERID; /* return value */
|
|
struct idtab *idt=NULL;
|
|
struct idhead *hp;
|
|
|
|
DUPRINT4(DB_RFSYS, "rglid: dev=%d, gp=%x, rid=%u\n", dev, gp, rid);
|
|
|
|
rd_lock();
|
|
|
|
if (gp && gp->idmap[dev])
|
|
idt = (struct idtab *) gp->idmap[dev];
|
|
else if (Global[dev])
|
|
idt = (struct idtab *) Global[dev];
|
|
else {
|
|
if (rid != defval)
|
|
ret = NO_ACCESS;
|
|
goto rglid_out; /* ret already has default value */
|
|
}
|
|
|
|
/* at this point should have a good table */
|
|
DUPRINT2(DB_RFSYS, "rglid: found table=%x\n", idt);
|
|
|
|
hp = (struct idhead *) idt;
|
|
size = idt->i_tblsiz;
|
|
defval = idt->i_defval;
|
|
|
|
/* check sets ret if it finds a match, if it doesn't, use default */
|
|
ret = (defval)?defval:rid;
|
|
|
|
if (size == 0) {
|
|
if (defval && rid != defval)
|
|
ret = NO_ACCESS;
|
|
goto rglid_out;
|
|
}
|
|
|
|
hp->i_tries++;
|
|
|
|
low = idt + HEADSIZE;
|
|
for (high = low + CACHESIZE; low < high; low++)
|
|
if (low->i_loc == rid) {
|
|
ret = low->i_rem;
|
|
hp->i_hits++;
|
|
goto rglid_out;
|
|
}
|
|
|
|
low = high;
|
|
for (high += size; low < high; low++)
|
|
if (low->i_loc == rid ||
|
|
(low->i_loc == UF && (low+1)->i_loc == rid)) {
|
|
ret = low->i_rem;
|
|
(idt+(hp->i_next))->i_loc = rid;
|
|
(idt+(hp->i_next))->i_rem = ret;
|
|
hp->i_next = (hp->i_next < (hp->i_cend - 1))?
|
|
hp->i_next + 1 : HEADSIZE;
|
|
break;
|
|
}
|
|
/*
|
|
* No match found. Need to insure that inaccessible ids are
|
|
* appropriately flagged. This is done by doing a forward
|
|
* mapping. If the forward mapping changes the input, it
|
|
* means that this id is inaccessible from this machine, and
|
|
* NO_ACCESS is returned.
|
|
*/
|
|
if (glid(dev, gp, rid) != rid) {
|
|
ret = NO_ACCESS;
|
|
(idt+(hp->i_next))->i_loc = rid;
|
|
(idt+(hp->i_next))->i_rem = ret;
|
|
hp->i_next = (hp->i_next < (hp->i_cend - 1))?
|
|
hp->i_next + 1 : HEADSIZE;
|
|
}
|
|
|
|
rglid_out:
|
|
rd_unlock();
|
|
DUPRINT2(DB_RFSYS, "rglid: returns %u\n", ret);
|
|
return (ret);
|
|
}
|