3837193fe5
the syscall arguments and does the suser() permission check, and kern_mlock(), which does the resource limit checking and calls vm_map_wire(). Split munlock() in a similar way. Enable the RLIMIT_MEMLOCK checking code in kern_mlock(). Replace calls to vslock() and vsunlock() in the sysctl code with calls to kern_mlock() and kern_munlock() so that the sysctl code will obey the wired memory limits. Nuke the vslock() and vsunlock() implementations, which are no longer used. Add a member to struct sysctl_req to track the amount of memory that is wired to handle the request. Modify sysctl_wire_old_buffer() to return an error if its call to kern_mlock() fails. Only wire the minimum of the length specified in the sysctl request and the length specified in its argument list. It is recommended that sysctl handlers that use sysctl_wire_old_buffer() should specify reasonable estimates for the amount of data they want to return so that only the minimum amount of memory is wired no matter what length has been specified by the request. Modify the callers of sysctl_wire_old_buffer() to look for the error return. Modify sysctl_old_user to obey the wired buffer length and clean up its implementation. Reviewed by: bms
672 lines
16 KiB
C
672 lines
16 KiB
C
/*
|
|
* 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.
|
|
*
|
|
* Connection tables
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#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/nwerror.h>
|
|
#include <netncp/ncp_subr.h>
|
|
#include <netncp/ncp_conn.h>
|
|
#include <netncp/ncp_sock.h>
|
|
#include <netncp/ncp_ncp.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 thread *td,
|
|
struct ucred *cred);
|
|
|
|
SYSCTL_DECL(_net_ncp);
|
|
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_destroy(void)
|
|
{
|
|
if (ncp_conn_cnt) {
|
|
NCPERROR("There are %d connections active\n", ncp_conn_cnt);
|
|
return EBUSY;
|
|
}
|
|
lockdestroy(&listlock);
|
|
lockdestroy(&lhlock);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ncp_conn_locklist(int flags, struct thread *td)
|
|
{
|
|
return lockmgr(&listlock, flags | LK_CANRECURSE, 0, td);
|
|
}
|
|
|
|
void
|
|
ncp_conn_unlocklist(struct thread *td)
|
|
{
|
|
lockmgr(&listlock, LK_RELEASE, 0, td);
|
|
}
|
|
|
|
int
|
|
ncp_conn_access(struct ncp_conn *conn, struct ucred *cred, mode_t mode)
|
|
{
|
|
int error;
|
|
|
|
if (cred == NOCRED || 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 thread *td, struct ucred *cred)
|
|
{
|
|
int error;
|
|
|
|
if (conn->nc_id == 0) return EACCES;
|
|
error = lockmgr(&conn->nc_lock, LK_EXCLUSIVE | LK_CANRECURSE, 0, td);
|
|
if (error == ERESTART)
|
|
return EINTR;
|
|
error = ncp_chkintr(conn, td);
|
|
if (error) {
|
|
lockmgr(&conn->nc_lock, LK_RELEASE, 0, td);
|
|
return error;
|
|
}
|
|
|
|
if (conn->nc_id == 0) {
|
|
lockmgr(&conn->nc_lock, LK_RELEASE, 0, td);
|
|
return EACCES;
|
|
}
|
|
conn->td = td; /* who currently operates */
|
|
conn->ucred = cred;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ncp_conn_lock(struct ncp_conn *conn, struct thread *td, struct ucred *cred, int mode)
|
|
{
|
|
int error;
|
|
|
|
error = ncp_conn_access(conn, cred, mode);
|
|
if (error) return error;
|
|
return ncp_conn_lock_any(conn, td, cred);
|
|
}
|
|
|
|
/*
|
|
* Lock conn but unlock connlist
|
|
*/
|
|
static int
|
|
ncp_conn_lock2(struct ncp_conn *conn, struct thread *td, struct ucred *cred, int mode)
|
|
{
|
|
int error;
|
|
|
|
error = ncp_conn_access(conn, cred, mode);
|
|
if (error) {
|
|
ncp_conn_unlocklist(td);
|
|
return error;
|
|
}
|
|
conn->nc_lwant++;
|
|
ncp_conn_unlocklist(td);
|
|
error = ncp_conn_lock_any(conn, td, cred);
|
|
conn->nc_lwant--;
|
|
if (conn->nc_lwant == 0) {
|
|
wakeup(&conn->nc_lwant);
|
|
}
|
|
return error;
|
|
}
|
|
|
|
void
|
|
ncp_conn_unlock(struct ncp_conn *conn, struct thread *td)
|
|
{
|
|
/*
|
|
* note, that LK_RELASE will do wakeup() instead of wakeup_one().
|
|
* this will do a little overhead
|
|
*/
|
|
lockmgr(&conn->nc_lock, LK_RELEASE, 0, td);
|
|
}
|
|
|
|
int
|
|
ncp_conn_assert_locked(struct ncp_conn *conn, const char *checker, struct thread *td)
|
|
{
|
|
if (conn->nc_lock.lk_flags & LK_HAVE_EXCL) return 0;
|
|
printf("%s: connection isn't locked!\n", checker);
|
|
return EIO;
|
|
}
|
|
|
|
void
|
|
ncp_conn_invalidate(struct ncp_conn *ncp)
|
|
{
|
|
ncp->flags &= ~(NCPFL_ATTACHED | NCPFL_LOGGED | NCPFL_INVALID);
|
|
}
|
|
|
|
int
|
|
ncp_conn_invalid(struct ncp_conn *ncp)
|
|
{
|
|
return ncp->flags & NCPFL_INVALID;
|
|
}
|
|
|
|
/*
|
|
* create, fill with defaults and return in locked state
|
|
*/
|
|
int
|
|
ncp_conn_alloc(struct ncp_conn_args *cap, struct thread *td, struct ucred *cred,
|
|
struct ncp_conn **conn)
|
|
{
|
|
struct ncp_conn *ncp;
|
|
struct ucred *owner;
|
|
int error, isroot;
|
|
|
|
if (cap->saddr.sa_family != AF_INET && cap->saddr.sa_family != AF_IPX)
|
|
return EPROTONOSUPPORT;
|
|
isroot = ncp_suser(cred) == 0;
|
|
/*
|
|
* Only root can change ownership
|
|
*/
|
|
if (cap->owner != NCP_DEFAULT_OWNER && !isroot)
|
|
return EPERM;
|
|
if (cap->group != NCP_DEFAULT_GROUP &&
|
|
!groupmember(cap->group, cred) && !isroot)
|
|
return EPERM;
|
|
if (cap->owner != NCP_DEFAULT_OWNER) {
|
|
owner = crget();
|
|
owner->cr_uid = cap->owner;
|
|
} else
|
|
owner = crhold(cred);
|
|
MALLOC(ncp, struct ncp_conn *, sizeof(struct ncp_conn),
|
|
M_NCPDATA, M_WAITOK | M_ZERO);
|
|
error = 0;
|
|
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->li = *cap;
|
|
ncp->nc_group = (cap->group != NCP_DEFAULT_GROUP) ?
|
|
cap->group : cred->cr_groups[0];
|
|
|
|
if (cap->retry_count == 0)
|
|
ncp->li.retry_count = NCP_RETRY_COUNT;
|
|
if (cap->timeout == 0)
|
|
ncp->li.timeout = NCP_RETRY_TIMEOUT;
|
|
ncp_conn_lock_any(ncp, td, ncp->nc_owner);
|
|
*conn = ncp;
|
|
ncp_conn_locklist(LK_EXCLUSIVE, td);
|
|
SLIST_INSERT_HEAD(&conn_list,ncp,nc_next);
|
|
ncp_conn_unlocklist(td);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* Remove the connection, on entry it must be locked
|
|
*/
|
|
int
|
|
ncp_conn_free(struct ncp_conn *ncp)
|
|
{
|
|
struct thread *td;
|
|
int error;
|
|
|
|
if (ncp == NULL) {
|
|
NCPFATAL("ncp == NULL\n");
|
|
return 0;
|
|
}
|
|
if (ncp->nc_id == 0) {
|
|
NCPERROR("nc_id == 0\n");
|
|
return EACCES;
|
|
}
|
|
td = ncp->td;
|
|
error = ncp_conn_assert_locked(ncp, __func__, td);
|
|
if (error)
|
|
return error;
|
|
if (ncp->ref_cnt != 0 || (ncp->flags & NCPFL_PERMANENT))
|
|
return EBUSY;
|
|
if (ncp_conn_access(ncp, ncp->ucred, NCPM_WRITE))
|
|
return EACCES;
|
|
|
|
if (ncp->flags & NCPFL_ATTACHED)
|
|
ncp_ncp_disconnect(ncp);
|
|
ncp_sock_disconnect(ncp);
|
|
|
|
/*
|
|
* Mark conn as dead and wait for other process
|
|
*/
|
|
ncp->nc_id = 0;
|
|
ncp_conn_unlock(ncp, td);
|
|
/*
|
|
* if signal is raised - how I do react ?
|
|
*/
|
|
lockmgr(&ncp->nc_lock, LK_DRAIN, 0, td);
|
|
lockdestroy(&ncp->nc_lock);
|
|
while (ncp->nc_lwant) {
|
|
printf("lwant = %d\n", ncp->nc_lwant);
|
|
tsleep(&ncp->nc_lwant, PZERO,"ncpdr",2*hz);
|
|
}
|
|
ncp_conn_locklist(LK_EXCLUSIVE, td);
|
|
SLIST_REMOVE(&conn_list, ncp, ncp_conn, nc_next);
|
|
ncp_conn_cnt--;
|
|
ncp_conn_unlocklist(td);
|
|
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);
|
|
}
|
|
|
|
int
|
|
ncp_conn_reconnect(struct ncp_conn *ncp)
|
|
{
|
|
int error;
|
|
|
|
/*
|
|
* Close opened sockets if any
|
|
*/
|
|
ncp_sock_disconnect(ncp);
|
|
error = ncp_sock_connect(ncp);
|
|
if (error)
|
|
return error;
|
|
error = ncp_ncp_connect(ncp);
|
|
if (error)
|
|
return error;
|
|
error = ncp_renegotiate_connparam(ncp, NCP_DEFAULT_BUFSIZE, 0);
|
|
if (error == NWE_SIGNATURE_LEVEL_CONFLICT) {
|
|
printf("Unable to negotiate requested security level\n");
|
|
error = EOPNOTSUPP;
|
|
}
|
|
if (error) {
|
|
ncp_ncp_disconnect(ncp);
|
|
return error;
|
|
}
|
|
#ifdef NCPBURST
|
|
error = ncp_burst_connect(ncp);
|
|
if (error) {
|
|
ncp_ncp_disconnect(ncp);
|
|
return error;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ncp_conn_login(struct ncp_conn *conn, struct thread *td, struct ucred *cred)
|
|
{
|
|
struct ncp_bindery_object user;
|
|
u_char ncp_key[8];
|
|
int error;
|
|
|
|
error = ncp_get_encryption_key(conn, ncp_key);
|
|
if (error) {
|
|
printf("%s: Warning: use unencrypted login\n", __func__);
|
|
error = ncp_login_unencrypted(conn, conn->li.objtype,
|
|
conn->li.user, conn->li.password, td, cred);
|
|
} else {
|
|
error = ncp_get_bindery_object_id(conn, conn->li.objtype,
|
|
conn->li.user, &user, td, cred);
|
|
if (error)
|
|
return error;
|
|
error = ncp_login_encrypted(conn, &user, ncp_key,
|
|
conn->li.password, td, cred);
|
|
}
|
|
if (!error)
|
|
conn->flags |= NCPFL_LOGGED | NCPFL_WASLOGGED;
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* Lookup connection by handle, return a locked conn descriptor
|
|
*/
|
|
int
|
|
ncp_conn_getbyref(int ref, struct thread *td, struct ucred *cred, int mode,
|
|
struct ncp_conn **connpp)
|
|
{
|
|
struct ncp_conn *ncp;
|
|
int error = 0;
|
|
|
|
ncp_conn_locklist(LK_SHARED, td);
|
|
SLIST_FOREACH(ncp, &conn_list, nc_next)
|
|
if (ncp->nc_id == ref) break;
|
|
if (ncp == NULL) {
|
|
ncp_conn_unlocklist(td);
|
|
return(EBADF);
|
|
}
|
|
error = ncp_conn_lock2(ncp, td, 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 thread *td,
|
|
struct ucred *cred, int mode, struct ncp_conn **connpp)
|
|
{
|
|
struct ncp_conn *ncp, *ncp2 = NULL;
|
|
int error = 0;
|
|
|
|
ncp_conn_locklist(LK_SHARED, td);
|
|
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(td);
|
|
return(EBADF);
|
|
}
|
|
error = ncp_conn_lock2(ncp, td, 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 thread *td,
|
|
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, td);
|
|
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(td);
|
|
return(EBADF);
|
|
}
|
|
error = ncp_conn_lock2(ncp, td, 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->td);
|
|
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->td);
|
|
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 thread *td, struct ncp_handle **handle)
|
|
{
|
|
struct ncp_handle *refp;
|
|
|
|
lockmgr(&lhlock, LK_EXCLUSIVE, 0, td);
|
|
SLIST_FOREACH(refp, &lhlist, nh_next)
|
|
if (refp->nh_conn == conn && td == refp->nh_td) break;
|
|
if (refp) {
|
|
conn->ref_cnt++;
|
|
refp->nh_ref++;
|
|
*handle = refp;
|
|
lockmgr(&lhlock, LK_RELEASE, 0, td);
|
|
return 0;
|
|
}
|
|
MALLOC(refp,struct ncp_handle *,sizeof(struct ncp_handle),M_NCPDATA,
|
|
M_WAITOK | M_ZERO);
|
|
SLIST_INSERT_HEAD(&lhlist,refp,nh_next);
|
|
refp->nh_ref++;
|
|
refp->nh_td = td;
|
|
refp->nh_conn = conn;
|
|
refp->nh_id = ncp_next_handle++;
|
|
*handle = refp;
|
|
conn->ref_cnt++;
|
|
lockmgr(&lhlock, LK_RELEASE, 0, td);
|
|
return 0;
|
|
}
|
|
/*
|
|
* release reference, if force - ignore refcount
|
|
*/
|
|
int
|
|
ncp_conn_puthandle(struct ncp_handle *handle, struct thread *td, int force)
|
|
{
|
|
struct ncp_handle *refp = handle;
|
|
|
|
lockmgr(&lhlock, LK_EXCLUSIVE, 0, td);
|
|
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, td);
|
|
return 0;
|
|
}
|
|
/*
|
|
* find a connHandle
|
|
*/
|
|
int
|
|
ncp_conn_findhandle(int connHandle, struct thread *td, struct ncp_handle **handle) {
|
|
struct ncp_handle *refp;
|
|
|
|
lockmgr(&lhlock, LK_SHARED, 0, td);
|
|
SLIST_FOREACH(refp, &lhlist, nh_next)
|
|
if (refp->nh_td == td && refp->nh_id == connHandle) break;
|
|
lockmgr(&lhlock, LK_RELEASE, 0, td);
|
|
if (refp == NULL) {
|
|
return EBADF;
|
|
}
|
|
*handle = refp;
|
|
return 0;
|
|
}
|
|
/*
|
|
* Clear handles associated with specified process
|
|
*/
|
|
int
|
|
ncp_conn_putprochandles(struct thread *td)
|
|
{
|
|
struct ncp_handle *hp, *nhp;
|
|
int haveone = 0;
|
|
|
|
lockmgr(&lhlock, LK_EXCLUSIVE, 0, td);
|
|
for (hp = SLIST_FIRST(&lhlist); hp; hp = nhp) {
|
|
nhp = SLIST_NEXT(hp, nh_next);
|
|
if (hp->nh_td != td) 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, td);
|
|
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->td->td_ucred;*/
|
|
|
|
error = sysctl_wire_old_buffer(req, 0);
|
|
if (error != 0)
|
|
return (error);
|
|
ncp_conn_locklist(LK_SHARED, req->td);
|
|
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->td);
|
|
return(error);
|
|
}
|