1
0
Files
irix-657m-src/eoe/cmd/sun/rpc.lockd/share.c
2022-09-29 17:59:04 +03:00

696 lines
16 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#if 0
static char sccsid[] = "@(#)prot_share.c 1.3 90/11/09 NFSSRC4.1 Copyr 1990 Sun Micro";
#endif
/*
* Copyright (c) 1986 by Sun Microsystems, Inc.
*/
/*
* prot_share.c consists of subroutines that implement the
* DOS-compatible file sharing services for PC-NFS
*/
#include <stdio.h>
#include <string.h>
#include <bstring.h>
#include <sys/file.h>
#ifdef __sgi
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <syslog.h>
#include <sys/syssgi.h>
#endif
#include "prot_lock.h"
#include "priv_prot.h"
extern int debug;
extern int report_sharing_conflicts;
extern int grace_period;
#ifdef __sgi
extern int lock_share_requests;
extern SVCXPRT *nlm_transp;
extern nlm_stats local_lock(struct reclock *a);
extern nlm_stats local_unlock(struct reclock *a);
#endif
#define SH_HASH 29
/*
* File sharing records are organized as follows:
*
* fh (file handle)
* |
* v
* hash()
* |
* +-------------+
* |
* v
* +-+-+-+-+-+-+-+-+-+-+-+-+-+
* fh_to_sx[]| | | | | | | | | | |*| | |
* +-+-+-+-+-+-+-+-+-+-+|+-+-+
* |
* +-----------------------------+
* |
* +--> +---+ +---+ +---+ +---+
* sx |FWD--->|FWD--->|FWD--->| 0 | (head of chains for files
* +---+ +---+ +---+ +---+ whose handle hash to the
* | 0 |<---BCK|<---BCK|<---BCK| same value)
* +---+ +---+ +---+ +---+
* |sxp| |sxp| |sxp| |sxp|
* +---+ +-|-+ +---+ +---+
* |
* +---------------+
* |
* +--> +---+ +---+ +---+ +---+
* sxx |FWD--->|FWD--->|FWD--->| 0 | (multiple shared access to
* +---+ +---+ +---+ +---+ the same file)
* | 0 |<---BCK|<---BCK|<---BCK|
* +---+ +---+ +---+ +---+
* | s | | s | | s | | s | (share: the structure
* | h | | h | | h | | h | passed in the RPC)
* | a | | a | | a | | a |
* | r | | r | | r | | r |
* | e | | e | | e | | e |
* +---+ +---+ +---+ +---+
*/
struct sxx {
struct sxx *fwd;
struct sxx *bck;
nlm_share sh;
};
struct sx {
struct sx *fwd;
struct sx *bck;
struct sxx *sxxp;
#ifdef __sgi
reclock *rlock;
#endif
};
static struct sx *fh_to_sx[SH_HASH];
static int share_reply ( SVCXPRT *T , nlm_shareargs *req , enum nlm_stats rc );
static int free_sxx ( struct sxx * );
static int free_sreq ( nlm_shareargs *r );
static struct sxx *new_sxx ( nlm_share *s );
static bool_t compatible ( struct sxx *ss , nlm_share *s );
#ifdef __sgi
static struct reclock *set_lock(nlm_shareargs *req);
static void free_lock(struct reclock *rl);
#endif
/* FIXME*FIXME*FIXME*FIXME*FIXME*FIXME*FIXME*FIXME*FIXME*FIXME*FIXME
*
* cmp_fh() compares file handles. It is not nice at all in that it
* presupposes to know what a file handle looks like. Basically, this
* is a screaming, last minute hack to keep ecd afloat, but it illustrates
* a weakness with the lock manager implementation. Specifically, the
* file handle was treated as a unique identifier in all of name space
* when in reality it was something quite different.
*/
static int
cmp_fh(a,b)
netobj *a, *b;
{
if (a->n_len != b->n_len)
return(FALSE);
return bcmp(&a->n_bytes[0], &b->n_bytes[0], 20) == 0;
}
/*
* There are three entry points into the prot_share
* subsystem:
*
* init_nlm_share
* called at the beginning of time to set up the hash table
*
* proc_nlm_share
* called out of nlm_prog() in prot_main.c to process a
* a SHARE or UNSHARE request
*
* destroy_client_shares
* called whenever a client has been declared dead by the
* status monitor. We throw away all sharing records
* for the client
*/
void *
init_nlm_share()
{
int i;
if (debug)
printf("init_nlm_share\n");
for (i = 0; i < SH_HASH; i++)
fh_to_sx[i] = NULL;
}
/*
* The following is the rough flow for proc_nlm_share:
*
* hv = hash(fh)
* sxh = fh_to_sx[hv];
* if (sxh!=NULL) {
* scan the chain looking for sxh->sxxp.sh.fh==fh
* }
* if (sxh==NULL) {
* SHARE: allocate new sx and sxx and chain to fh_to_sx[hv]
* UNSHARE: give up
* }
* sxxh = sxh->sxxp
* scan the chain looking for (sxxh->sh.caller_mame==caller_name &&
* sxxh->sh.oh == oh) [i.e. the matching share structure]
* if (sxxh!=NULL) {
* SHARE: return {SUCCESS} [assume repeat request]
* UNSHARE: release *sxxh, which may free sxh, which may lead
* to the fh_to_sx entry being reset
* }
* [there is no existing entry]
* UNSHARE: give up
* SHARE: scan chain starting at sxxxh->sxxp to see if the sharing
* is legal. If not, fail. Otherwise allocate new
* sxx, initialize it and add it to the list.
* Return {SUCCESS}
*/
void *
proc_nlm_share(Rqstp, Transp)
struct svc_req *Rqstp;
SVCXPRT *Transp;
{
nlm_shareargs *req;
struct sx *sxh;
struct sxx *sxxh;
int hv;
enum nlm_stats result;
bool_t SHARE_REQ;
register unsigned char *c;
register int i;
/*
* Remember what kind of request this was
*/
SHARE_REQ = (Rqstp->rq_proc == NLM_SHARE);
#define UNSHARE_REQ (!SHARE_REQ)
/*
* Allocate space for arguments and decode them
*/
req = (nlm_shareargs *)xmalloc(sizeof(nlm_shareargs));
if (!svc_getargs(Transp, xdr_nlm_shareargs, req)) {
free_sreq(req);
svcerr_decode(Transp);
return;
}
if (debug) {
printf("proc_nlm_share: %s from %s\n",
SHARE_REQ?"share":"unshare",
req->share.caller_name);
printf("oh="); pr_fh(&req->share.oh);
printf("\nfh="); pr_fh(&req->share.fh);
printf("\n");
if (SHARE_REQ)
printf("share: mode=%d, access=%d\n",
req->share.mode, req->share.access);
}
/*
* We only allow reclaims during the grace period
*/
if (grace_period > 0 && !(req->reclaim)) {
result = nlm_denied_grace_period;
goto common_exit;
}
/*
* Compute the hash index and get the start of the sxx chain
*/
hv = 0;
i = req->share.fh.n_len;
if (i > 20) i = 20;
for (c = (unsigned char *) req->share.fh.n_bytes; --i;)
hv += *c++;
hv %= SH_HASH;
sxh = fh_to_sx[hv];
/*
* If sxh != NULL, scan down looking for the fh we are dealing with
*/
while (sxh != NULL) {
if (cmp_fh(&req->share.fh, &sxh->sxxp->sh.fh))
break;
sxh = sxh->fwd;
}
/*
* If sxh is null, we're not currently holding a sharing state for
* this file. If it's a SHARE request, allocate new sx & sxx structures
*/
if (sxh == NULL) {
if (SHARE_REQ) {
#ifdef __sgi
struct reclock *rl = NULL;
if (lock_share_requests) {
/* fsm_DN is essentially a request to NOT lock the
* file, so we just grant it...
*/
if ((req->share.mode == fsm_DN) &&
(req->share.access == fsa_NONE))
rl = NULL;
else if ((rl = set_lock(req)) == 0) {
if (debug)
printf("...set_lock failed\n");
result = nlm_denied;
goto common_exit;
} else if (debug)
printf("...set_lock succeeded!\n");
}
#endif
if (debug)
printf("...null sxh - new file\n");
sxh = (struct sx *)malloc(sizeof(struct sx));
sxh->bck = NULL;
sxh->fwd = fh_to_sx[hv];
fh_to_sx[hv] = sxh;
sxh->sxxp = new_sxx(&req->share);
#ifdef __sgi
sxh->rlock = rl;
#endif
}
result = nlm_granted; /* either way.... */
goto common_exit;
}
/*
* If sxh is non-null, we have sharing state for this file.
* Let's try and find an sx which corresponds exactly to this
* request.
*/
if (debug)
printf("...sxh @0x%x\n", sxh);
sxxh = sxh->sxxp;
while (sxxh != NULL) {
if (!strcasecmp(sxxh->sh.caller_name, req->share.caller_name) &&
obj_cmp(&sxxh->sh.oh, &req->share.oh))
break;
sxxh = sxxh->fwd;
}
/*
* If sxxh is non-null, we have found an entry corresponding to
* the request. For SHARE, we just reply "nlm_granted" -- presumably
* this is an artefact of UDP-based RPC's. For UNSHARE, we must
* throw away the sxx, and maybe the sx.
*/
if (sxxh != NULL) {
if (debug)printf("...matching sxx @0x%x\n", sxxh);
if (UNSHARE_REQ) {
/*
* Routine list manipulation follows....
*/
if (sxxh->bck == NULL)
sxh->sxxp = sxxh->fwd;
else
sxxh->bck->fwd = sxxh->fwd;
if (sxxh->fwd != NULL)
sxxh->fwd->bck = sxxh->bck;
free_sxx(sxxh);
/*
* Check to see if we need to eliminate the sxx as well
*/
if (sxh->sxxp == NULL) {
if (sxh->bck == NULL)
fh_to_sx[hv] = sxh->fwd;
else
sxh->bck->fwd = sxh->fwd;
if (sxh->fwd != NULL)
sxh->fwd->bck = sxh->bck;
#ifdef __sgi
/*
* Free the lock and close the fd now
*/
if (lock_share_requests)
free_lock(sxh->rlock);
#endif
free((char *)sxh);
}
}
result = nlm_granted; /* for SHARE or UNSHARE */
goto common_exit;
}
/*
* We've now established that there's no existing sx corresponding
* to this request. For UNSHARE, we just give "ok".... For
* SHARE we've got to validate this sharing request. If it's o.k.
* we build a new sx and add it to the list. [Current thinking
* is that there's no special ordering here, so we can just stick it
* on the front.]
*/
if (SHARE_REQ) {
if (!compatible(sxh->sxxp, &req->share)) {
if (debug)
printf("...not compatible\n");
result = nlm_denied;
goto common_exit;
}
sxxh = new_sxx(&req->share);
if (debug)printf("...compatible. new sxx @0x%x\n", sxxh);
sxh->sxxp->bck = sxxh;
sxxh->fwd = sxh->sxxp;
sxh->sxxp = sxxh;
}
result = nlm_granted; /* SHARE/UNSHARE. Now we fall through to... */
/*
* Common exit - issue reply with appropriate result code,
* free request structure and quit
*/
common_exit:
share_reply(Transp, req, result);
free_sreq(req);
}
/*
* The following is the rough flow for destroy_client_shares:
*
* for (hv = 0; hv < SH_HASH; hv++){
* sxh = fh_to_sx[hv];
* while (sxh!=NULL) {
* sxxh = sxh->sxxp
* while (sxxh != NULL) {
* if (!strcmp(sxxh->sh.caller_name,client))
* blow away the entry (may longjump out of loops)
* sxxh=sxxh->fwd
* }
* sxh=sxh->fwd;
* }
*/
void *
destroy_client_shares(client)
char *client;
{
struct sx *sxh;
struct sx *next_sx;
struct sxx *sxxh;
struct sxx *next_sxx;
int hv;
if (debug)
printf("destroy_client_shares for %s\n", client);
for (hv = 0; hv < SH_HASH; hv++) {
sxh = fh_to_sx[hv];
/*
* If sxxh != NULL, scan down looking for the fh we are dealing with
*/
while (sxh != NULL) {
sxxh = sxh->sxxp;
next_sx = sxh->fwd;
while (sxxh != NULL) {
next_sxx = sxxh->fwd;
if (!strcasecmp(sxxh->sh.caller_name, client)) {
/*
* Blow this one away....
*/
if (debug)
printf("... zap sxx @0x%x\n", sxxh);
if (sxxh->bck == NULL)
sxh->sxxp = sxxh->fwd;
else
sxxh->bck->fwd = sxxh->fwd;
if (sxxh->fwd != NULL)
sxxh->fwd->bck = sxxh->bck;
free_sxx(sxxh);
/*
* Check to see if we need to eliminate the sxx as well
*/
if (sxh->sxxp == NULL) {
if (debug)
printf("... & sx @0x%x\n", sxh);
if (sxh->bck == NULL)
fh_to_sx[hv] = sxh->fwd;
else
sxh->bck->fwd = sxh->fwd;
if (sxh->fwd != NULL)
sxh->fwd->bck = sxh->bck;
#ifdef __sgi
/*
* Release the associated lock and close the fd
*/
if (lock_share_requests)
free_lock(sxh->rlock);
#endif
free((char *)sxh);
goto skip_to_next_sx;
}
} /* ... of "if (strcmp..." */
sxxh = next_sxx;
} /* of "while (sxxh..." */
skip_to_next_sx:
sxh = next_sx;
} /* ... of "while (sxh..." */
} /* ... of "for (hv... " */
}
/*
* ------ Support routines -----
*/
static
share_reply(T,req,rc)
SVCXPRT *T;
nlm_shareargs *req;
enum nlm_stats rc;
{
nlm_shareres res;
res.cookie.n_len = req->cookie.n_len;
res.cookie.n_bytes = req->cookie.n_bytes;
res.stat = rc;
if (!(svc_sendreply(T, xdr_nlm_shareres, &res)))
svcerr_systemerr(T);
}
static int
free_sxx(struct sxx *s)
{
xfree(&s->sh.caller_name);
xfree(&s->sh.fh.n_bytes);
xfree(&s->sh.oh.n_bytes);
free(s);
}
static
free_sreq(r)
nlm_shareargs *r;
{
if (r) {
xfree(&r->cookie.n_bytes);
xfree(&r->share.caller_name);
xfree(&r->share.fh.n_bytes);
xfree(&r->share.oh.n_bytes);
}
free(r);
}
/*
* Allocate a new sxx structure based on that in the RPC. We make use
* of the fact that the caller-name, fh and oh are dynamically
* allocated, and that we won't need them in the original copy after
* this point in time, so we just copy the pointers and lengths and
* set the pointers to NULL (xfree will check them in free_sreq)
*/
static struct sxx *
new_sxx(s)
nlm_share *s;
{
struct sxx *new;
new = (struct sxx *)malloc(sizeof(struct sxx));
new->fwd = NULL;
new->bck = NULL;
new->sh.caller_name = s->caller_name;
s->caller_name = NULL;
new->sh.fh.n_len = s->fh.n_len;
new->sh.fh.n_bytes = s->fh.n_bytes;
s->fh.n_bytes = NULL;
new->sh.oh.n_len = s->oh.n_len;
new->sh.oh.n_bytes = s->oh.n_bytes;
s->oh.n_bytes = NULL;
new->sh.mode = s->mode;
new->sh.access = s->access;
return(new);
}
/*
* compatible - the guts of the whole thing. Takes a pointer to
* the first sx structure in the list and one to the share structure
* in the RPC. Returns TRUE if the SHARE is compatible with the
* existing state.
*
* The checking relies on the bit encoding for fsh_mode and
* fsh_access (see nlm_prot.h). Specifically,
* if any existing access ANDed with the requested mode is non-zero, OR
* if any existing mode ANDed with the requested access is non-zero,
* we deny the SHARE.
*/
static bool_t
compatible(ss,s)
struct sxx *ss;
nlm_share *s;
{
while (ss != NULL) {
if (((int)ss->sh.mode & (int)s->access) ||
((int)ss->sh.access & (int)s->mode)) {
if (report_sharing_conflicts)
printf("\nFile sharing request from %s (mode %d, access %d) conflicts\nwith sharing for %s (mode %d, access %d)\n",
s->caller_name, s->mode, s->access,
ss->sh.caller_name, ss->sh.mode, ss->sh.access);
return(FALSE);
}
ss = ss->fwd;
}
return(TRUE);
}
#ifdef __sgi
#ifndef max
#define max(a,b) (((a)>(b))?(a):(b))
#endif
static int nlm_shareargstoreclock(nlm_shareargs *from, struct reclock *to)
{
fsh_mode mode;
if (from->cookie.n_len) {
if (obj_copy(&to->cookie, &from->cookie) == -1)
return(-1);
} else
to->cookie.n_bytes= NULL;
/* Never block on SHARE requests */
to->block = FALSE;
mode = from->share.mode;
/* Novell's NFS Client doesn't set the mode if access is fsa_R;
* To be sure, we beef up the mode according to access.
*/
switch (from->share.access) {
case fsa_R:
mode = max(mode, fsm_DW);
break;
case fsa_W:
case fsa_RW:
mode = max(mode, fsm_DRW);
break;
}
switch (mode) {
case fsm_DR:
/* Technically, writing should still be allowed. There is no
* analogous lock in Unix, however. Is this important in practice?
*/
case fsm_DRW:
to->exclusive = TRUE;
to->alock.lox.type = F_WRLCK;
break;
case fsm_DW:
to->exclusive = FALSE;
to->alock.lox.type = F_RDLCK;
break;
case fsm_DN:
/* Just to be sure; these will be reset according to access */
to->exclusive = FALSE;
to->alock.lox.type = 0;
break;
}
if ((to->alock.caller_name = copy_str(from->share.caller_name)) == NULL)
return(-1);
if (from->share.fh.n_len) {
if (obj_copy(&to->alock.fh, &from->share.fh) == -1)
return(-1);
} else
to->alock.fh.n_len = 0;
if (from->share.oh.n_len) {
if (obj_copy(&to->alock.oh, &from->share.oh) == -1)
return(-1);
} else
to->alock.oh.n_len = 0;
to->alock.lox.base = 0;
to->alock.lox.length = 0;
to->alock.lox.pid = -2;
to->alock.lox.ipaddr = nlm_transp->xp_raddr.sin_addr.s_addr;
to->reclaim = from->reclaim;
return(0);
}
/* Given a share request attempt to set the lock.
* If successful, return the fd; otherwise return 0
*/
static struct reclock *set_lock(nlm_shareargs *req)
{
struct reclock *rl;
nlm_stats stat;
if ((rl = get_reclock()) == NULL)
return 0;
if (nlm_shareargstoreclock(req, rl) == -1)
goto bail;
stat = local_lock(rl);
if (stat == nlm_granted)
return rl;
bail:
release_reclock(rl);
return 0;
}
/* If no more share state exists for this file, free the lock.
* Otherwise do nothing, as someone else still has this file locked.
*/
static void free_lock(struct reclock *rl)
{
if (rl) {
local_unlock(rl);
release_reclock(rl);
}
}
#endif