Pass LK_PCATCH instead of trying to store tsleep flags in the lock
[dragonfly.git] / sys / netproto / ncp / ncp_conn.c
blobe5b3fa4c4825473d937115b34e9a2fe2d654fab1
1 /*
2 * Copyright (c) 1999, Boris Popov
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by Boris Popov.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 * $FreeBSD: src/sys/netncp/ncp_conn.c,v 1.3.2.5 2001/02/22 08:54:11 bp Exp $
32 * $DragonFly: src/sys/netproto/ncp/ncp_conn.c,v 1.9 2006/03/02 19:07:59 dillon Exp $
34 * Connection tables
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/malloc.h>
40 #include <sys/proc.h>
41 #include <sys/lock.h>
42 #include <sys/sysctl.h>
44 #include "ncp.h"
45 #include "ncp_subr.h"
46 #include "ncp_conn.h"
48 SLIST_HEAD(ncp_handle_head,ncp_handle);
50 int ncp_burst_enabled = 1;
52 struct ncp_conn_head conn_list={NULL};
53 static int ncp_conn_cnt = 0;
54 static int ncp_next_ref = 1;
55 static struct lock listlock;
57 struct ncp_handle_head lhlist={NULL};
58 static int ncp_next_handle = 1;
59 static struct lock lhlock;
61 static int ncp_sysctl_connstat(SYSCTL_HANDLER_ARGS);
62 static int ncp_conn_lock_any(struct ncp_conn *conn, struct thread *td,
63 struct ucred *cred);
65 SYSCTL_DECL(_net_ncp);
66 SYSCTL_INT (_net_ncp, OID_AUTO, burst_enabled, CTLFLAG_RD, &ncp_burst_enabled, 0, "");
67 SYSCTL_INT (_net_ncp, OID_AUTO, conn_cnt, CTLFLAG_RD, &ncp_conn_cnt, 0, "");
68 SYSCTL_PROC(_net_ncp, OID_AUTO, conn_stat, CTLFLAG_RD|CTLTYPE_OPAQUE,
69 NULL, 0, ncp_sysctl_connstat, "S,connstat", "Connections list");
71 MALLOC_DEFINE(M_NCPDATA, "NCP data", "NCP private data");
73 int
74 ncp_conn_init(void) {
75 lockinit(&listlock, "ncpll", 0, 0);
76 lockinit(&lhlock, "ncplh", 0, 0);
77 return 0;
80 int
81 ncp_conn_locklist(int flags, struct thread *td)
83 return lockmgr(&listlock, flags | LK_CANRECURSE, NULL, td);
86 void
87 ncp_conn_unlocklist(struct thread *td)
89 lockmgr(&listlock, LK_RELEASE, NULL, td);
92 int
93 ncp_conn_access(struct ncp_conn *conn, struct ucred *cred, mode_t mode)
95 int error;
97 if (cred == NOCRED || ncp_suser(cred) == 0 ||
98 cred->cr_uid == conn->nc_owner->cr_uid)
99 return 0;
100 mode >>= 3;
101 if (!groupmember(conn->nc_group, cred))
102 mode >>= 3;
103 error = (conn->li.access_mode & mode) == mode ? 0 : EACCES;
104 return error;
108 ncp_conn_lock_any(struct ncp_conn *conn, struct thread *td, struct ucred *cred)
110 int error;
112 if (conn->nc_id == 0) return EACCES;
113 error = lockmgr(&conn->nc_lock, LK_EXCLUSIVE | LK_CANRECURSE, NULL, td);
114 if (error == ERESTART)
115 return EINTR;
116 error = ncp_chkintr(conn, td);
117 if (error) {
118 lockmgr(&conn->nc_lock, LK_RELEASE, NULL, td);
119 return error;
122 if (conn->nc_id == 0) {
123 lockmgr(&conn->nc_lock, LK_RELEASE, NULL, td);
124 return EACCES;
126 conn->td = td; /* who currently operates */
127 conn->ucred = cred;
128 return 0;
132 ncp_conn_lock(struct ncp_conn *conn, struct thread *td,
133 struct ucred *cred, int mode)
135 int error;
137 error = ncp_conn_access(conn, cred, mode);
138 if (error)
139 return error;
140 return ncp_conn_lock_any(conn, td, cred);
144 * Lock conn but unlock connlist
146 static int
147 ncp_conn_lock2(struct ncp_conn *conn, struct thread *td,
148 struct ucred *cred, int mode)
150 int error;
152 error = ncp_conn_access(conn, cred, mode);
153 if (error) {
154 ncp_conn_unlocklist(td);
155 return error;
157 conn->nc_lwant++;
158 ncp_conn_unlocklist(td);
159 error = ncp_conn_lock_any(conn, td, cred);
160 conn->nc_lwant--;
161 if (conn->nc_lwant == 0) {
162 wakeup(&conn->nc_lwant);
164 return error;
167 void
168 ncp_conn_unlock(struct ncp_conn *conn, struct thread *td) {
170 * note, that LK_RELASE will do wakeup() instead of wakeup_one().
171 * this will do a little overhead
173 lockmgr(&conn->nc_lock, LK_RELEASE, NULL, td);
176 int
177 ncp_conn_assert_locked(struct ncp_conn *conn, const char *checker,
178 struct thread *td)
180 if (conn->nc_lock.lk_flags & LK_HAVE_EXCL)
181 return 0;
182 printf("%s: connection isn't locked!\n", checker);
183 return EIO;
187 * create, fill with defaults and return in locked state
190 ncp_conn_alloc(struct thread *td, struct ucred *cred, struct ncp_conn **conn)
192 int error;
193 struct ncp_conn *ncp;
195 MALLOC(ncp, struct ncp_conn *, sizeof(struct ncp_conn),
196 M_NCPDATA, M_WAITOK | M_ZERO);
197 error = 0;
198 lockinit(&ncp->nc_lock, "ncplck", 0, 0);
199 ncp_conn_cnt++;
200 ncp->nc_id = ncp_next_ref++;
201 ncp->nc_owner = cred;
202 ncp->seq = 0;
203 ncp->connid = 0xFFFF;
204 ncp_conn_lock_any(ncp, td, ncp->nc_owner);
205 *conn = ncp;
206 ncp_conn_locklist(LK_EXCLUSIVE, td);
207 SLIST_INSERT_HEAD(&conn_list,ncp,nc_next);
208 ncp_conn_unlocklist(td);
209 return (error);
213 * Remove the connection, on entry it must be locked
216 ncp_conn_free(struct ncp_conn *ncp)
218 int error;
219 struct ncp_conn *ncp1;
221 if (ncp->nc_id == 0) {
222 printf("already!!!!\n");
223 return EACCES;
225 if (ncp==NULL) {
226 NCPFATAL("conn==NULL !\n");
227 return(EIO);
229 error = ncp_conn_assert_locked(ncp, __func__, ncp->td);
230 if (error) return error;
231 if (ncp->ref_cnt) {
232 NCPFATAL("there are %d referenses left\n",ncp->ref_cnt);
233 return(EBUSY);
236 * Mark conn as died and wait for other process
238 ncp->nc_id = 0;
239 ncp_conn_unlock(ncp, ncp->td);
241 * if signal is raised - how I do react ?
243 lockmgr(&ncp->nc_lock, LK_DRAIN, NULL, ncp->td);
244 while (ncp->nc_lwant) {
245 printf("lwant = %d\n", ncp->nc_lwant);
246 tsleep(&ncp->nc_lwant, 0, "ncpdr", 2*hz);
248 ncp_conn_locklist(LK_EXCLUSIVE, ncp->td);
250 * It is possible, that other process destroy connection while we draining,
251 * and free it. So, we must rescan list
253 SLIST_FOREACH(ncp1, &conn_list, nc_next) {
254 if (ncp1 == ncp) break;
256 if (ncp1 == NULL) {
257 ncp_conn_unlocklist(ncp->td);
258 return 0;
260 SLIST_REMOVE(&conn_list, ncp, ncp_conn, nc_next);
261 ncp_conn_cnt--;
262 ncp_conn_unlocklist(ncp->td);
263 if (ncp->li.user) free(ncp->li.user, M_NCPDATA);
264 if (ncp->li.password) free(ncp->li.password, M_NCPDATA);
265 crfree(ncp->nc_owner);
266 FREE(ncp, M_NCPDATA);
267 return (0);
271 * Lookup connection by handle, return a locked conn descriptor
274 ncp_conn_getbyref(int ref,struct thread *td,struct ucred *cred, int mode, struct ncp_conn **connpp){
275 struct ncp_conn *ncp;
276 int error=0;
278 ncp_conn_locklist(LK_SHARED, td);
279 SLIST_FOREACH(ncp, &conn_list, nc_next)
280 if (ncp->nc_id == ref) break;
281 if (ncp == NULL) {
282 ncp_conn_unlocklist(td);
283 return(EBADF);
285 error = ncp_conn_lock2(ncp, td, cred, mode);
286 if (!error)
287 *connpp = ncp;
288 return (error);
291 * find attached, but not logged in connection to specified server
294 ncp_conn_getattached(struct ncp_conn_args *li,struct thread *td,struct ucred *cred,int mode, struct ncp_conn **connpp){
295 struct ncp_conn *ncp, *ncp2=NULL;
296 int error = 0;
298 ncp_conn_locklist(LK_SHARED, td);
299 SLIST_FOREACH(ncp, &conn_list, nc_next) {
300 if ((ncp->flags & NCPFL_LOGGED) != 0 ||
301 strcmp(ncp->li.server,li->server) != 0 ||
302 ncp->li.saddr.sa_len != li->saddr.sa_len ||
303 bcmp(&ncp->li.saddr,&ncp->li.saddr,li->saddr.sa_len) != 0)
304 continue;
305 if (ncp_suser(cred) == 0 ||
306 cred->cr_uid == ncp->nc_owner->cr_uid)
307 break;
308 error = ncp_conn_access(ncp,cred,mode);
309 if (!error && ncp2 == NULL)
310 ncp2 = ncp;
312 if (ncp == NULL) ncp = ncp2;
313 if (ncp == NULL) {
314 ncp_conn_unlocklist(td);
315 return(EBADF);
317 error = ncp_conn_lock2(ncp,td,cred,mode);
318 if (!error)
319 *connpp=ncp;
320 return (error);
324 * Lookup connection by server/user pair, return a locked conn descriptor.
325 * if li is NULL or server/user pair incomplete, try to select best connection
326 * based on owner.
327 * Connection selected in next order:
328 * 1. Try to search conn with ucred owner, if li is NULL also find a primary
329 * 2. If 1. fails try to get first suitable shared connection
330 * 3. If 2. fails then nothing can help to poor ucred owner
334 ncp_conn_getbyli(struct ncp_conn_args *li, struct thread *td,
335 struct ucred *cred, int mode, struct ncp_conn **connpp)
337 struct ncp_conn *ncp, *ncp2=NULL;
338 int error=0, partial, haveserv;
340 partial = (li == NULL || li->server[0] == 0 || li->user == NULL);
341 haveserv = (li && li->server[0]);
342 ncp_conn_locklist(LK_SHARED, td);
343 SLIST_FOREACH(ncp, &conn_list, nc_next) {
344 if (partial) {
345 if (cred->cr_uid == ncp->nc_owner->cr_uid) {
346 if (haveserv) {
347 if (strcmp(ncp->li.server,li->server) == 0)
348 break;
349 } else {
350 if (ncp->flags & NCPFL_PRIMARY)
351 break;
352 ncp2 = ncp;
354 continue;
356 } else {
357 if (strcmp(ncp->li.server,li->server) != 0 ||
358 ncp->li.user == NULL ||
359 strcmp(ncp->li.user,li->user) != 0)
360 continue;
361 if (cred->cr_uid == ncp->nc_owner->cr_uid)
362 break;
363 if (ncp_suser(cred) == 0)
364 ncp2 = ncp;
366 error = ncp_conn_access(ncp,cred,mode);
367 if (!error && ncp2 == NULL)
368 ncp2 = ncp;
370 if (ncp == NULL) ncp = ncp2;
371 if (ncp == NULL) {
372 ncp_conn_unlocklist(td);
373 return(EBADF);
375 error = ncp_conn_lock2(ncp,td,cred,mode);
376 if (!error)
377 *connpp=ncp;
378 return (error);
382 * Set primary connection flag, since it have sence only for an owner,
383 * only owner can modify this flag.
384 * connection expected to be locked.
387 ncp_conn_setprimary(struct ncp_conn *conn, int on){
388 struct ncp_conn *ncp=NULL;
390 if (conn->ucred->cr_uid != conn->nc_owner->cr_uid)
391 return EACCES;
392 ncp_conn_locklist(LK_SHARED, conn->td);
393 SLIST_FOREACH(ncp, &conn_list, nc_next) {
394 if (conn->ucred->cr_uid == ncp->nc_owner->cr_uid)
395 ncp->flags &= ~NCPFL_PRIMARY;
397 ncp_conn_unlocklist(conn->td);
398 if (on)
399 conn->flags |= NCPFL_PRIMARY;
400 return 0;
403 * Lease conn to given proc, returning unique handle
404 * problem: how locks should be applied ?
407 ncp_conn_gethandle(struct ncp_conn *conn, struct thread *td, struct ncp_handle **handle){
408 struct ncp_handle *refp;
410 lockmgr(&lhlock, LK_EXCLUSIVE, NULL, td);
411 SLIST_FOREACH(refp, &lhlist, nh_next)
412 if (refp->nh_conn == conn && td == refp->nh_td) break;
413 if (refp) {
414 conn->ref_cnt++;
415 refp->nh_ref++;
416 *handle = refp;
417 lockmgr(&lhlock, LK_RELEASE, NULL, td);
418 return 0;
420 MALLOC(refp,struct ncp_handle *,sizeof(struct ncp_handle),M_NCPDATA,
421 M_WAITOK | M_ZERO);
422 SLIST_INSERT_HEAD(&lhlist,refp,nh_next);
423 refp->nh_ref++;
424 refp->nh_td = td;
425 refp->nh_conn = conn;
426 refp->nh_id = ncp_next_handle++;
427 *handle = refp;
428 conn->ref_cnt++;
429 lockmgr(&lhlock, LK_RELEASE, NULL, td);
430 return 0;
433 * release reference, if force - ignore refcount
436 ncp_conn_puthandle(struct ncp_handle *handle, struct thread *td, int force) {
437 struct ncp_handle *refp = handle;
439 lockmgr(&lhlock, LK_EXCLUSIVE, NULL, td);
440 refp->nh_ref--;
441 refp->nh_conn->ref_cnt--;
442 if (force) {
443 refp->nh_conn->ref_cnt -= refp->nh_ref;
444 refp->nh_ref = 0;
446 if (refp->nh_ref == 0) {
447 SLIST_REMOVE(&lhlist, refp, ncp_handle, nh_next);
448 FREE(refp, M_NCPDATA);
450 lockmgr(&lhlock, LK_RELEASE, NULL, td);
451 return 0;
454 * find a connHandle
457 ncp_conn_findhandle(int connHandle, struct thread *td, struct ncp_handle **handle) {
458 struct ncp_handle *refp;
460 lockmgr(&lhlock, LK_SHARED, NULL, td);
461 SLIST_FOREACH(refp, &lhlist, nh_next)
462 if (refp->nh_td == td && refp->nh_id == connHandle) break;
463 lockmgr(&lhlock, LK_RELEASE, NULL, td);
464 if (refp == NULL) {
465 return EBADF;
467 *handle = refp;
468 return 0;
471 * Clear handles associated with specified process
474 ncp_conn_putprochandles(struct thread *td) {
475 struct ncp_handle *hp, *nhp;
476 int haveone = 0;
478 lockmgr(&lhlock, LK_EXCLUSIVE, NULL, td);
479 for (hp = SLIST_FIRST(&lhlist); hp; hp = nhp) {
480 nhp = SLIST_NEXT(hp, nh_next);
481 if (hp->nh_td != td) continue;
482 haveone = 1;
483 hp->nh_conn->ref_cnt -= hp->nh_ref;
484 SLIST_REMOVE(&lhlist, hp, ncp_handle, nh_next);
485 FREE(hp, M_NCPDATA);
487 lockmgr(&lhlock, LK_RELEASE, NULL, td);
488 return haveone;
491 * remove references in all possible connections,
492 * XXX - possible problem is a locked list.
494 /*void
495 ncp_conn_list_rm_ref(pid_t pid) {
496 struct ncp_conn *ncp;
498 ncp_conn_locklist(LK_SHARED, NULL);
499 SLIST_FOREACH(ncp, &conn_list, nc_next) {
500 ncp_conn_rm_ref(ncp,pid,1);
502 ncp_conn_unlocklist(NULL);
503 return;
507 ncp_conn_getinfo(struct ncp_conn *ncp, struct ncp_conn_stat *ncs) {
508 bzero(ncs,sizeof(*ncs));
509 ncs->li = ncp->li;
510 ncs->li.user = ncs->user;
511 if (ncp->li.user)
512 strcpy(ncs->user, ncp->li.user);
513 ncs->li.password = NULL;
514 ncs->connRef = ncp->nc_id;
515 ncs->ref_cnt = ncp->ref_cnt;
516 ncs->connid = ncp->connid;
517 ncs->owner = ncp->nc_owner->cr_uid;
518 ncs->group = ncp->nc_group;
519 ncs->flags = ncp->flags;
520 ncs->buffer_size = ncp->buffer_size;
521 return 0;
524 static int
525 ncp_sysctl_connstat(SYSCTL_HANDLER_ARGS) {
526 int error;
527 struct ncp_conn_stat ncs;
528 struct ncp_conn *ncp;
529 /* struct ucred *cred = req->p->p_ucred;*/
531 error = 0;
532 ncp_conn_locklist(LK_SHARED, req->td);
533 error = SYSCTL_OUT(req, &ncp_conn_cnt, sizeof(ncp_conn_cnt));
534 SLIST_FOREACH(ncp, &conn_list, nc_next) {
535 if (error) break;
536 /* I can't do conn_lock while list is locked */
537 ncp->nc_lwant++;
538 if (!error) {
539 ncp_conn_getinfo(ncp, &ncs);
540 } else {
541 bzero(&ncs,sizeof(ncs));
542 ncs.connRef = ncp->nc_id;
543 strcpy(ncs.li.server,"***");
545 ncp->nc_lwant--;
546 error = SYSCTL_OUT(req, &ncs, sizeof(ncs));
548 ncp_conn_unlocklist(req->td);
549 return(error);