diff options
Diffstat (limited to 'sys/netncp/ncp_conn.c')
-rw-r--r-- | sys/netncp/ncp_conn.c | 539 |
1 files changed, 539 insertions, 0 deletions
diff --git a/sys/netncp/ncp_conn.c b/sys/netncp/ncp_conn.c new file mode 100644 index 0000000..835176e --- /dev/null +++ b/sys/netncp/ncp_conn.c @@ -0,0 +1,539 @@ +/* + * Copyright (c) 1999, Boris Popov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Boris Popov. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * $FreeBSD$ + * + * Connection tables + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/proc.h> +#include <sys/lock.h> +#include <sys/sysctl.h> + +#include <netncp/ncp.h> +#include <netncp/ncp_subr.h> +#include <netncp/ncp_conn.h> + +SLIST_HEAD(ncp_handle_head,ncp_handle); + +int ncp_burst_enabled = 1; + +struct ncp_conn_head conn_list={NULL}; +static int ncp_conn_cnt = 0; +static int ncp_next_ref = 1; +static struct lock listlock; + +struct ncp_handle_head lhlist={NULL}; +static int ncp_next_handle = 1; +static struct lock lhlock; + +static int ncp_sysctl_connstat SYSCTL_HANDLER_ARGS; +static int ncp_conn_lock_any(struct ncp_conn *conn, struct proc *p, + struct ucred *cred); + +extern struct linker_set sysctl_net_ncp; + +SYSCTL_DECL(_net_ncp); +SYSCTL_NODE(_net, OID_AUTO, ncp, CTLFLAG_RW, NULL, "NetWare requester"); +SYSCTL_INT (_net_ncp, OID_AUTO, burst_enabled, CTLFLAG_RD, &ncp_burst_enabled, 0, ""); +SYSCTL_INT (_net_ncp, OID_AUTO, conn_cnt, CTLFLAG_RD, &ncp_conn_cnt, 0, ""); +SYSCTL_PROC(_net_ncp, OID_AUTO, conn_stat, CTLFLAG_RD|CTLTYPE_OPAQUE, + NULL, 0, ncp_sysctl_connstat, "S,connstat", "Connections list"); + +MALLOC_DEFINE(M_NCPDATA, "NCP data", "NCP private data"); + +int +ncp_conn_init(void) { + lockinit(&listlock, PSOCK, "ncpll", 0, 0); + lockinit(&lhlock, PSOCK, "ncplh", 0, 0); + return 0; +} + +int +ncp_conn_locklist(int flags, struct proc *p){ + return lockmgr(&listlock, flags | LK_CANRECURSE, 0, p); +} + +void +ncp_conn_unlocklist(struct proc *p){ + lockmgr(&listlock, LK_RELEASE, 0, p); +} + +int +ncp_conn_access(struct ncp_conn *conn, struct ucred *cred, mode_t mode) { + int error; + + if (ncp_suser(cred) == 0 || cred->cr_uid == conn->nc_owner->cr_uid) + return 0; + mode >>= 3; + if (!groupmember(conn->nc_group, cred)) + mode >>= 3; + error = (conn->li.access_mode & mode) == mode ? 0 : EACCES; + return error; +} + +int +ncp_conn_lock_any(struct ncp_conn *conn, struct proc *p, struct ucred *cred) { + int error; + + if (conn->nc_id == 0) return EACCES; + error = lockmgr(&conn->nc_lock, LK_EXCLUSIVE | LK_CANRECURSE, 0, p); + if (error == ERESTART) + return EINTR; + error = ncp_chkintr(conn, p); + if (error) { + lockmgr(&conn->nc_lock, LK_RELEASE, 0, p); + return error; + } + + if (conn->nc_id == 0) { + lockmgr(&conn->nc_lock, LK_RELEASE, 0, p); + return EACCES; + } + conn->procp = p; /* who currently operates */ + conn->ucred = cred; + return 0; +} + +int +ncp_conn_lock(struct ncp_conn *conn, struct proc *p, struct ucred *cred, int mode) { + int error; + + error = ncp_conn_access(conn,cred,mode); + if (error) return error; + return ncp_conn_lock_any(conn, p, cred); +} + +/* + * Lock conn but unlock connlist + */ +static int +ncp_conn_lock2(struct ncp_conn *conn, struct proc *p, struct ucred *cred, int mode) { + int error; + + error = ncp_conn_access(conn,cred,mode); + if (error) { + ncp_conn_unlocklist(p); + return error; + } + conn->nc_lwant++; + ncp_conn_unlocklist(p); + error = ncp_conn_lock_any(conn,p,cred); + conn->nc_lwant--; + if (conn->nc_lwant == 0) { + wakeup(&conn->nc_lwant); + } + return error; +} + +void +ncp_conn_unlock(struct ncp_conn *conn, struct proc *p) { + /* + * note, that LK_RELASE will do wakeup() instead of wakeup_one(). + * this will do a little overhead + */ + lockmgr(&conn->nc_lock, LK_RELEASE, 0, p); +} + +int +ncp_conn_assert_locked(struct ncp_conn *conn,char *checker, struct proc *p){ + if (conn->nc_lock.lk_flags & LK_HAVE_EXCL) return 0; + printf("%s: connection isn't locked!\n", checker); + return EIO; +} + +/* + * create, fill with defaults and return in locked state + */ +int +ncp_conn_alloc(struct proc *p, struct ucred *cred, struct ncp_conn **conn) +{ + int error; + struct ncp_conn *ncp; + + MALLOC(ncp, struct ncp_conn *, sizeof(struct ncp_conn), + M_NCPDATA, M_WAITOK); + if (ncp == NULL) return ENOMEM; + error = 0; + bzero(ncp,sizeof(*ncp)); + lockinit(&ncp->nc_lock, PZERO, "ncplck", 0, 0); + ncp_conn_cnt++; + ncp->nc_id = ncp_next_ref++; + ncp->nc_owner = cred; + ncp->seq = 0; + ncp->connid = 0xFFFF; + ncp_conn_lock_any(ncp, p, ncp->nc_owner); + *conn = ncp; + ncp_conn_locklist(LK_EXCLUSIVE, p); + SLIST_INSERT_HEAD(&conn_list,ncp,nc_next); + ncp_conn_unlocklist(p); + return (error); +} + +/* + * Remove the connection, on entry it must be locked + */ +int +ncp_conn_free(struct ncp_conn *ncp) { + int error; + struct ncp_conn *ncp1; + + if (ncp->nc_id == 0) { + printf("already!!!!\n"); + return EACCES; + } + if (ncp==NULL) { + NCPFATAL("conn==NULL !\n"); + return(EIO); + } + error = ncp_conn_assert_locked(ncp, __FUNCTION__, ncp->procp); + if (error) return error; + if (ncp->ref_cnt) { + NCPFATAL("there are %d referenses left\n",ncp->ref_cnt); + return(EBUSY); + } + /* + * Mark conn as died and wait for other process + */ + ncp->nc_id = 0; + ncp_conn_unlock(ncp,ncp->procp); + /* + * if signal is raised - how I do react ? + */ + lockmgr(&ncp->nc_lock, LK_DRAIN, 0, ncp->procp); + while (ncp->nc_lwant) { + printf("lwant = %d\n", ncp->nc_lwant); + tsleep(&ncp->nc_lwant, PZERO,"ncpdr",2*hz); + } + ncp_conn_locklist(LK_EXCLUSIVE, ncp->procp); + /* + * It is possible, that other process destroy connection while we draining, + * and free it. So, we must rescan list + */ + SLIST_FOREACH(ncp1, &conn_list, nc_next) { + if (ncp1 == ncp) break; + } + if (ncp1 == NULL) { + ncp_conn_unlocklist(ncp->procp); + return 0; + } + SLIST_REMOVE(&conn_list, ncp, ncp_conn, nc_next); + ncp_conn_cnt--; + ncp_conn_unlocklist(ncp->procp); + if (ncp->li.user) free(ncp->li.user, M_NCPDATA); + if (ncp->li.password) free(ncp->li.password, M_NCPDATA); + crfree(ncp->nc_owner); + FREE(ncp, M_NCPDATA); + return (0); +} + +/* + * Lookup connection by handle, return a locked conn descriptor + */ +int +ncp_conn_getbyref(int ref,struct proc *p,struct ucred *cred, int mode, struct ncp_conn **connpp){ + struct ncp_conn *ncp; + int error=0; + + ncp_conn_locklist(LK_SHARED, p); + SLIST_FOREACH(ncp, &conn_list, nc_next) + if (ncp->nc_id == ref) break; + if (ncp == NULL) { + ncp_conn_unlocklist(p); + return(EBADF); + } + error = ncp_conn_lock2(ncp, p, cred, mode); + if (!error) + *connpp = ncp; + return (error); +} +/* + * find attached, but not logged in connection to specified server + */ +int +ncp_conn_getattached(struct ncp_conn_args *li,struct proc *p,struct ucred *cred,int mode, struct ncp_conn **connpp){ + struct ncp_conn *ncp, *ncp2=NULL; + int error = 0; + + ncp_conn_locklist(LK_SHARED, p); + SLIST_FOREACH(ncp, &conn_list, nc_next) { + if ((ncp->flags & NCPFL_LOGGED) != 0 || + strcmp(ncp->li.server,li->server) != 0 || + ncp->li.saddr.sa_len != li->saddr.sa_len || + bcmp(&ncp->li.saddr,&ncp->li.saddr,li->saddr.sa_len) != 0) + continue; + if (ncp_suser(cred) == 0 || + cred->cr_uid == ncp->nc_owner->cr_uid) + break; + error = ncp_conn_access(ncp,cred,mode); + if (!error && ncp2 == NULL) + ncp2 = ncp; + } + if (ncp == NULL) ncp = ncp2; + if (ncp == NULL) { + ncp_conn_unlocklist(p); + return(EBADF); + } + error = ncp_conn_lock2(ncp,p,cred,mode); + if (!error) + *connpp=ncp; + return (error); +} + +/* + * Lookup connection by server/user pair, return a locked conn descriptor. + * if li is NULL or server/user pair incomplete, try to select best connection + * based on owner. + * Connection selected in next order: + * 1. Try to search conn with ucred owner, if li is NULL also find a primary + * 2. If 1. fails try to get first suitable shared connection + * 3. If 2. fails then nothing can help to poor ucred owner + */ + +int +ncp_conn_getbyli(struct ncp_conn_args *li,struct proc *p,struct ucred *cred,int mode, struct ncp_conn **connpp){ + struct ncp_conn *ncp, *ncp2=NULL; + int error=0, partial, haveserv; + + partial = (li == NULL || li->server[0] == 0 || li->user == NULL); + haveserv = (li && li->server[0]); + ncp_conn_locklist(LK_SHARED, p); + SLIST_FOREACH(ncp, &conn_list, nc_next) { + if (partial) { + if (cred->cr_uid == ncp->nc_owner->cr_uid) { + if (haveserv) { + if (strcmp(ncp->li.server,li->server) == 0) + break; + } else { + if (ncp->flags & NCPFL_PRIMARY) + break; + ncp2 = ncp; + } + continue; + } + } else { + if (strcmp(ncp->li.server,li->server) != 0 || + ncp->li.user == NULL || + strcmp(ncp->li.user,li->user) != 0) + continue; + if (cred->cr_uid == ncp->nc_owner->cr_uid) + break; + if (ncp_suser(cred) == 0) + ncp2 = ncp; + } + error = ncp_conn_access(ncp,cred,mode); + if (!error && ncp2 == NULL) + ncp2 = ncp; + } + if (ncp == NULL) ncp = ncp2; + if (ncp == NULL) { + ncp_conn_unlocklist(p); + return(EBADF); + } + error = ncp_conn_lock2(ncp,p,cred,mode); + if (!error) + *connpp=ncp; + return (error); +} + +/* + * Set primary connection flag, since it have sence only for an owner, + * only owner can modify this flag. + * connection expected to be locked. + */ +int +ncp_conn_setprimary(struct ncp_conn *conn, int on){ + struct ncp_conn *ncp=NULL; + + if (conn->ucred->cr_uid != conn->nc_owner->cr_uid) + return EACCES; + ncp_conn_locklist(LK_SHARED, conn->procp); + SLIST_FOREACH(ncp, &conn_list, nc_next) { + if (conn->ucred->cr_uid == ncp->nc_owner->cr_uid) + ncp->flags &= ~NCPFL_PRIMARY; + } + ncp_conn_unlocklist(conn->procp); + if (on) + conn->flags |= NCPFL_PRIMARY; + return 0; +} +/* + * Lease conn to given proc, returning unique handle + * problem: how locks should be applied ? + */ +int +ncp_conn_gethandle(struct ncp_conn *conn, struct proc *p, struct ncp_handle **handle){ + struct ncp_handle *refp; + + lockmgr(&lhlock, LK_EXCLUSIVE, 0, p); + SLIST_FOREACH(refp, &lhlist, nh_next) + if (refp->nh_conn == conn && p == refp->nh_proc) break; + if (refp) { + conn->ref_cnt++; + refp->nh_ref++; + *handle = refp; + lockmgr(&lhlock, LK_RELEASE, 0, p); + return 0; + } + MALLOC(refp,struct ncp_handle *,sizeof(struct ncp_handle),M_NCPDATA,M_WAITOK); + if (refp == NULL) return ENOMEM; + bzero(refp,sizeof(*refp)); + SLIST_INSERT_HEAD(&lhlist,refp,nh_next); + refp->nh_ref++; + refp->nh_proc = p; + refp->nh_conn = conn; + refp->nh_id = ncp_next_handle++; + *handle = refp; + conn->ref_cnt++; + lockmgr(&lhlock, LK_RELEASE, 0, p); + return 0; +} +/* + * release reference, if force - ignore refcount + */ +int +ncp_conn_puthandle(struct ncp_handle *handle, struct proc *p, int force) { + struct ncp_handle *refp = handle; + + lockmgr(&lhlock, LK_EXCLUSIVE, 0, p); + refp->nh_ref--; + refp->nh_conn->ref_cnt--; + if (force) { + refp->nh_conn->ref_cnt -= refp->nh_ref; + refp->nh_ref = 0; + } + if (refp->nh_ref == 0) { + SLIST_REMOVE(&lhlist, refp, ncp_handle, nh_next); + FREE(refp, M_NCPDATA); + } + lockmgr(&lhlock, LK_RELEASE, 0, p); + return 0; +} +/* + * find a connHandle + */ +int +ncp_conn_findhandle(int connHandle, struct proc *p, struct ncp_handle **handle) { + struct ncp_handle *refp; + + lockmgr(&lhlock, LK_SHARED, 0, p); + SLIST_FOREACH(refp, &lhlist, nh_next) + if (refp->nh_proc == p && refp->nh_id == connHandle) break; + lockmgr(&lhlock, LK_RELEASE, 0, p); + if (refp == NULL) { + return EBADF; + } + *handle = refp; + return 0; +} +/* + * Clear handles associated with specified process + */ +int +ncp_conn_putprochandles(struct proc *p) { + struct ncp_handle *hp, *nhp; + int haveone = 0; + + lockmgr(&lhlock, LK_EXCLUSIVE, 0, p); + for (hp = lhlist.slh_first; hp; hp = nhp) { + nhp = hp->nh_next.sle_next; + if (hp->nh_proc != p) continue; + haveone = 1; + hp->nh_conn->ref_cnt -= hp->nh_ref; + SLIST_REMOVE(&lhlist, hp, ncp_handle, nh_next); + FREE(hp, M_NCPDATA); + } + lockmgr(&lhlock, LK_RELEASE, 0, p); + return haveone; +} +/* + * remove references in all possible connections, + * XXX - possible problem is a locked list. + */ +/*void +ncp_conn_list_rm_ref(pid_t pid) { + struct ncp_conn *ncp; + + ncp_conn_locklist(LK_SHARED, NULL); + SLIST_FOREACH(ncp, &conn_list, nc_next) { + ncp_conn_rm_ref(ncp,pid,1); + } + ncp_conn_unlocklist(NULL); + return; +} +*/ +int +ncp_conn_getinfo(struct ncp_conn *ncp, struct ncp_conn_stat *ncs) { + bzero(ncs,sizeof(*ncs)); + ncs->li = ncp->li; + ncs->li.user = ncs->user; + if (ncp->li.user) + strcpy(ncs->user, ncp->li.user); + ncs->li.password = NULL; + ncs->connRef = ncp->nc_id; + ncs->ref_cnt = ncp->ref_cnt; + ncs->connid = ncp->connid; + ncs->owner = ncp->nc_owner->cr_uid; + ncs->group = ncp->nc_group; + ncs->flags = ncp->flags; + ncs->buffer_size = ncp->buffer_size; + return 0; +} + +static int +ncp_sysctl_connstat SYSCTL_HANDLER_ARGS { + int error; + struct ncp_conn_stat ncs; + struct ncp_conn *ncp; +/* struct ucred *cred = req->p->p_ucred;*/ + + error = 0; + ncp_conn_locklist(LK_SHARED, req->p); + error = SYSCTL_OUT(req, &ncp_conn_cnt, sizeof(ncp_conn_cnt)); + SLIST_FOREACH(ncp, &conn_list, nc_next) { + if (error) break; + /* I can't do conn_lock while list is locked */ + ncp->nc_lwant++; + if (!error) { + ncp_conn_getinfo(ncp, &ncs); + } else { + bzero(&ncs,sizeof(ncs)); + ncs.connRef = ncp->nc_id; + strcpy(ncs.li.server,"***"); + } + ncp->nc_lwant--; + error = SYSCTL_OUT(req, &ncs, sizeof(ncs)); + } + ncp_conn_unlocklist(req->p); + return(error); +} |