proc->thread stage 4: rework the VFS and DEVICE subsystems to take thread
[dragonfly.git] / sys / netproto / ncp / ncp_conn.c
CommitLineData
984263bc
MD
1/*
2 * Copyright (c) 1999, Boris Popov
3 * All rights reserved.
4 *
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.
19 *
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 $
dadab5e9 32 * $DragonFly: src/sys/netproto/ncp/ncp_conn.c,v 1.3 2003/06/25 03:56:05 dillon Exp $
984263bc
MD
33 *
34 * Connection tables
35 */
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>
43
44#include <netncp/ncp.h>
45#include <netncp/ncp_subr.h>
46#include <netncp/ncp_conn.h>
47
48SLIST_HEAD(ncp_handle_head,ncp_handle);
49
50int ncp_burst_enabled = 1;
51
52struct ncp_conn_head conn_list={NULL};
53static int ncp_conn_cnt = 0;
54static int ncp_next_ref = 1;
55static struct lock listlock;
56
57struct ncp_handle_head lhlist={NULL};
58static int ncp_next_handle = 1;
59static struct lock lhlock;
60
61static int ncp_sysctl_connstat(SYSCTL_HANDLER_ARGS);
dadab5e9 62static int ncp_conn_lock_any(struct ncp_conn *conn, struct thread *td,
984263bc
MD
63 struct ucred *cred);
64
65extern struct linker_set sysctl_net_ncp;
66
67SYSCTL_DECL(_net_ncp);
68SYSCTL_INT (_net_ncp, OID_AUTO, burst_enabled, CTLFLAG_RD, &ncp_burst_enabled, 0, "");
69SYSCTL_INT (_net_ncp, OID_AUTO, conn_cnt, CTLFLAG_RD, &ncp_conn_cnt, 0, "");
70SYSCTL_PROC(_net_ncp, OID_AUTO, conn_stat, CTLFLAG_RD|CTLTYPE_OPAQUE,
71 NULL, 0, ncp_sysctl_connstat, "S,connstat", "Connections list");
72
73MALLOC_DEFINE(M_NCPDATA, "NCP data", "NCP private data");
74
75int
76ncp_conn_init(void) {
77 lockinit(&listlock, PSOCK, "ncpll", 0, 0);
78 lockinit(&lhlock, PSOCK, "ncplh", 0, 0);
79 return 0;
80}
81
82int
dadab5e9
MD
83ncp_conn_locklist(int flags, struct thread *td)
84{
85 return lockmgr(&listlock, flags | LK_CANRECURSE, 0, td);
984263bc
MD
86}
87
88void
dadab5e9
MD
89ncp_conn_unlocklist(struct thread *td)
90{
91 lockmgr(&listlock, LK_RELEASE, 0, td);
984263bc
MD
92}
93
94int
dadab5e9
MD
95ncp_conn_access(struct ncp_conn *conn, struct ucred *cred, mode_t mode)
96{
984263bc
MD
97 int error;
98
99 if (cred == NOCRED || ncp_suser(cred) == 0 ||
100 cred->cr_uid == conn->nc_owner->cr_uid)
101 return 0;
102 mode >>= 3;
103 if (!groupmember(conn->nc_group, cred))
104 mode >>= 3;
105 error = (conn->li.access_mode & mode) == mode ? 0 : EACCES;
106 return error;
107}
108
109int
dadab5e9
MD
110ncp_conn_lock_any(struct ncp_conn *conn, struct thread *td, struct ucred *cred)
111{
984263bc
MD
112 int error;
113
114 if (conn->nc_id == 0) return EACCES;
dadab5e9 115 error = lockmgr(&conn->nc_lock, LK_EXCLUSIVE | LK_CANRECURSE, 0, td);
984263bc
MD
116 if (error == ERESTART)
117 return EINTR;
dadab5e9 118 error = ncp_chkintr(conn, td);
984263bc 119 if (error) {
dadab5e9 120 lockmgr(&conn->nc_lock, LK_RELEASE, 0, td);
984263bc
MD
121 return error;
122 }
123
124 if (conn->nc_id == 0) {
dadab5e9 125 lockmgr(&conn->nc_lock, LK_RELEASE, 0, td);
984263bc
MD
126 return EACCES;
127 }
dadab5e9 128 conn->td = td; /* who currently operates */
984263bc
MD
129 conn->ucred = cred;
130 return 0;
131}
132
133int
dadab5e9
MD
134ncp_conn_lock(struct ncp_conn *conn, struct thread *td,
135 struct ucred *cred, int mode)
136{
984263bc
MD
137 int error;
138
dadab5e9
MD
139 error = ncp_conn_access(conn, cred, mode);
140 if (error)
141 return error;
142 return ncp_conn_lock_any(conn, td, cred);
984263bc
MD
143}
144
145/*
146 * Lock conn but unlock connlist
147 */
148static int
dadab5e9
MD
149ncp_conn_lock2(struct ncp_conn *conn, struct thread *td,
150 struct ucred *cred, int mode)
151{
984263bc
MD
152 int error;
153
dadab5e9 154 error = ncp_conn_access(conn, cred, mode);
984263bc 155 if (error) {
dadab5e9 156 ncp_conn_unlocklist(td);
984263bc
MD
157 return error;
158 }
159 conn->nc_lwant++;
dadab5e9
MD
160 ncp_conn_unlocklist(td);
161 error = ncp_conn_lock_any(conn, td, cred);
984263bc
MD
162 conn->nc_lwant--;
163 if (conn->nc_lwant == 0) {
164 wakeup(&conn->nc_lwant);
165 }
166 return error;
167}
168
169void
dadab5e9 170ncp_conn_unlock(struct ncp_conn *conn, struct thread *td) {
984263bc
MD
171 /*
172 * note, that LK_RELASE will do wakeup() instead of wakeup_one().
173 * this will do a little overhead
174 */
dadab5e9 175 lockmgr(&conn->nc_lock, LK_RELEASE, 0, td);
984263bc
MD
176}
177
178int
dadab5e9
MD
179ncp_conn_assert_locked(struct ncp_conn *conn,char *checker, struct thread *td)
180{
181 if (conn->nc_lock.lk_flags & LK_HAVE_EXCL)
182 return 0;
984263bc
MD
183 printf("%s: connection isn't locked!\n", checker);
184 return EIO;
185}
186
187/*
188 * create, fill with defaults and return in locked state
189 */
190int
dadab5e9 191ncp_conn_alloc(struct thread *td, struct ucred *cred, struct ncp_conn **conn)
984263bc
MD
192{
193 int error;
194 struct ncp_conn *ncp;
195
196 MALLOC(ncp, struct ncp_conn *, sizeof(struct ncp_conn),
197 M_NCPDATA, M_WAITOK | M_ZERO);
198 error = 0;
199 lockinit(&ncp->nc_lock, PZERO, "ncplck", 0, 0);
200 ncp_conn_cnt++;
201 ncp->nc_id = ncp_next_ref++;
202 ncp->nc_owner = cred;
203 ncp->seq = 0;
204 ncp->connid = 0xFFFF;
dadab5e9 205 ncp_conn_lock_any(ncp, td, ncp->nc_owner);
984263bc 206 *conn = ncp;
dadab5e9 207 ncp_conn_locklist(LK_EXCLUSIVE, td);
984263bc 208 SLIST_INSERT_HEAD(&conn_list,ncp,nc_next);
dadab5e9 209 ncp_conn_unlocklist(td);
984263bc
MD
210 return (error);
211}
212
213/*
214 * Remove the connection, on entry it must be locked
215 */
216int
dadab5e9
MD
217ncp_conn_free(struct ncp_conn *ncp)
218{
984263bc
MD
219 int error;
220 struct ncp_conn *ncp1;
221
222 if (ncp->nc_id == 0) {
dadab5e9 223 printf("already!!!!\n");
984263bc
MD
224 return EACCES;
225 }
226 if (ncp==NULL) {
227 NCPFATAL("conn==NULL !\n");
228 return(EIO);
229 }
dadab5e9 230 error = ncp_conn_assert_locked(ncp, __FUNCTION__, ncp->td);
984263bc
MD
231 if (error) return error;
232 if (ncp->ref_cnt) {
233 NCPFATAL("there are %d referenses left\n",ncp->ref_cnt);
234 return(EBUSY);
235 }
236 /*
237 * Mark conn as died and wait for other process
238 */
239 ncp->nc_id = 0;
dadab5e9 240 ncp_conn_unlock(ncp, ncp->td);
984263bc
MD
241 /*
242 * if signal is raised - how I do react ?
243 */
dadab5e9 244 lockmgr(&ncp->nc_lock, LK_DRAIN, 0, ncp->td);
984263bc
MD
245 while (ncp->nc_lwant) {
246 printf("lwant = %d\n", ncp->nc_lwant);
247 tsleep(&ncp->nc_lwant, PZERO,"ncpdr",2*hz);
248 }
dadab5e9 249 ncp_conn_locklist(LK_EXCLUSIVE, ncp->td);
984263bc
MD
250 /*
251 * It is possible, that other process destroy connection while we draining,
252 * and free it. So, we must rescan list
253 */
254 SLIST_FOREACH(ncp1, &conn_list, nc_next) {
255 if (ncp1 == ncp) break;
256 }
257 if (ncp1 == NULL) {
dadab5e9 258 ncp_conn_unlocklist(ncp->td);
984263bc
MD
259 return 0;
260 }
261 SLIST_REMOVE(&conn_list, ncp, ncp_conn, nc_next);
262 ncp_conn_cnt--;
dadab5e9 263 ncp_conn_unlocklist(ncp->td);
984263bc
MD
264 if (ncp->li.user) free(ncp->li.user, M_NCPDATA);
265 if (ncp->li.password) free(ncp->li.password, M_NCPDATA);
266 crfree(ncp->nc_owner);
267 FREE(ncp, M_NCPDATA);
268 return (0);
269}
270
271/*
272 * Lookup connection by handle, return a locked conn descriptor
273 */
274int
dadab5e9 275ncp_conn_getbyref(int ref,struct thread *td,struct ucred *cred, int mode, struct ncp_conn **connpp){
984263bc
MD
276 struct ncp_conn *ncp;
277 int error=0;
278
dadab5e9 279 ncp_conn_locklist(LK_SHARED, td);
984263bc
MD
280 SLIST_FOREACH(ncp, &conn_list, nc_next)
281 if (ncp->nc_id == ref) break;
282 if (ncp == NULL) {
dadab5e9 283 ncp_conn_unlocklist(td);
984263bc
MD
284 return(EBADF);
285 }
dadab5e9 286 error = ncp_conn_lock2(ncp, td, cred, mode);
984263bc
MD
287 if (!error)
288 *connpp = ncp;
289 return (error);
290}
291/*
292 * find attached, but not logged in connection to specified server
293 */
294int
dadab5e9 295ncp_conn_getattached(struct ncp_conn_args *li,struct thread *td,struct ucred *cred,int mode, struct ncp_conn **connpp){
984263bc
MD
296 struct ncp_conn *ncp, *ncp2=NULL;
297 int error = 0;
298
dadab5e9 299 ncp_conn_locklist(LK_SHARED, td);
984263bc
MD
300 SLIST_FOREACH(ncp, &conn_list, nc_next) {
301 if ((ncp->flags & NCPFL_LOGGED) != 0 ||
302 strcmp(ncp->li.server,li->server) != 0 ||
303 ncp->li.saddr.sa_len != li->saddr.sa_len ||
304 bcmp(&ncp->li.saddr,&ncp->li.saddr,li->saddr.sa_len) != 0)
305 continue;
306 if (ncp_suser(cred) == 0 ||
307 cred->cr_uid == ncp->nc_owner->cr_uid)
308 break;
309 error = ncp_conn_access(ncp,cred,mode);
310 if (!error && ncp2 == NULL)
311 ncp2 = ncp;
312 }
313 if (ncp == NULL) ncp = ncp2;
314 if (ncp == NULL) {
dadab5e9 315 ncp_conn_unlocklist(td);
984263bc
MD
316 return(EBADF);
317 }
dadab5e9 318 error = ncp_conn_lock2(ncp,td,cred,mode);
984263bc
MD
319 if (!error)
320 *connpp=ncp;
321 return (error);
322}
323
324/*
325 * Lookup connection by server/user pair, return a locked conn descriptor.
326 * if li is NULL or server/user pair incomplete, try to select best connection
327 * based on owner.
328 * Connection selected in next order:
329 * 1. Try to search conn with ucred owner, if li is NULL also find a primary
330 * 2. If 1. fails try to get first suitable shared connection
331 * 3. If 2. fails then nothing can help to poor ucred owner
332 */
333
334int
dadab5e9
MD
335ncp_conn_getbyli(struct ncp_conn_args *li, struct thread *td,
336 struct ucred *cred, int mode, struct ncp_conn **connpp)
337{
984263bc
MD
338 struct ncp_conn *ncp, *ncp2=NULL;
339 int error=0, partial, haveserv;
340
341 partial = (li == NULL || li->server[0] == 0 || li->user == NULL);
342 haveserv = (li && li->server[0]);
dadab5e9 343 ncp_conn_locklist(LK_SHARED, td);
984263bc
MD
344 SLIST_FOREACH(ncp, &conn_list, nc_next) {
345 if (partial) {
346 if (cred->cr_uid == ncp->nc_owner->cr_uid) {
347 if (haveserv) {
348 if (strcmp(ncp->li.server,li->server) == 0)
349 break;
350 } else {
351 if (ncp->flags & NCPFL_PRIMARY)
352 break;
353 ncp2 = ncp;
354 }
355 continue;
356 }
357 } else {
358 if (strcmp(ncp->li.server,li->server) != 0 ||
359 ncp->li.user == NULL ||
360 strcmp(ncp->li.user,li->user) != 0)
361 continue;
362 if (cred->cr_uid == ncp->nc_owner->cr_uid)
363 break;
364 if (ncp_suser(cred) == 0)
365 ncp2 = ncp;
366 }
367 error = ncp_conn_access(ncp,cred,mode);
368 if (!error && ncp2 == NULL)
369 ncp2 = ncp;
370 }
371 if (ncp == NULL) ncp = ncp2;
372 if (ncp == NULL) {
dadab5e9 373 ncp_conn_unlocklist(td);
984263bc
MD
374 return(EBADF);
375 }
dadab5e9 376 error = ncp_conn_lock2(ncp,td,cred,mode);
984263bc
MD
377 if (!error)
378 *connpp=ncp;
379 return (error);
380}
381
382/*
383 * Set primary connection flag, since it have sence only for an owner,
384 * only owner can modify this flag.
385 * connection expected to be locked.
386 */
387int
388ncp_conn_setprimary(struct ncp_conn *conn, int on){
389 struct ncp_conn *ncp=NULL;
390
391 if (conn->ucred->cr_uid != conn->nc_owner->cr_uid)
392 return EACCES;
dadab5e9 393 ncp_conn_locklist(LK_SHARED, conn->td);
984263bc
MD
394 SLIST_FOREACH(ncp, &conn_list, nc_next) {
395 if (conn->ucred->cr_uid == ncp->nc_owner->cr_uid)
396 ncp->flags &= ~NCPFL_PRIMARY;
397 }
dadab5e9 398 ncp_conn_unlocklist(conn->td);
984263bc
MD
399 if (on)
400 conn->flags |= NCPFL_PRIMARY;
401 return 0;
402}
403/*
404 * Lease conn to given proc, returning unique handle
405 * problem: how locks should be applied ?
406 */
407int
dadab5e9 408ncp_conn_gethandle(struct ncp_conn *conn, struct thread *td, struct ncp_handle **handle){
984263bc
MD
409 struct ncp_handle *refp;
410
dadab5e9 411 lockmgr(&lhlock, LK_EXCLUSIVE, 0, td);
984263bc 412 SLIST_FOREACH(refp, &lhlist, nh_next)
dadab5e9 413 if (refp->nh_conn == conn && td == refp->nh_td) break;
984263bc
MD
414 if (refp) {
415 conn->ref_cnt++;
416 refp->nh_ref++;
417 *handle = refp;
dadab5e9 418 lockmgr(&lhlock, LK_RELEASE, 0, td);
984263bc
MD
419 return 0;
420 }
421 MALLOC(refp,struct ncp_handle *,sizeof(struct ncp_handle),M_NCPDATA,
422 M_WAITOK | M_ZERO);
423 SLIST_INSERT_HEAD(&lhlist,refp,nh_next);
424 refp->nh_ref++;
dadab5e9 425 refp->nh_td = td;
984263bc
MD
426 refp->nh_conn = conn;
427 refp->nh_id = ncp_next_handle++;
428 *handle = refp;
429 conn->ref_cnt++;
dadab5e9 430 lockmgr(&lhlock, LK_RELEASE, 0, td);
984263bc
MD
431 return 0;
432}
433/*
434 * release reference, if force - ignore refcount
435 */
436int
dadab5e9 437ncp_conn_puthandle(struct ncp_handle *handle, struct thread *td, int force) {
984263bc
MD
438 struct ncp_handle *refp = handle;
439
dadab5e9 440 lockmgr(&lhlock, LK_EXCLUSIVE, 0, td);
984263bc
MD
441 refp->nh_ref--;
442 refp->nh_conn->ref_cnt--;
443 if (force) {
444 refp->nh_conn->ref_cnt -= refp->nh_ref;
445 refp->nh_ref = 0;
446 }
447 if (refp->nh_ref == 0) {
448 SLIST_REMOVE(&lhlist, refp, ncp_handle, nh_next);
449 FREE(refp, M_NCPDATA);
450 }
dadab5e9 451 lockmgr(&lhlock, LK_RELEASE, 0, td);
984263bc
MD
452 return 0;
453}
454/*
455 * find a connHandle
456 */
457int
dadab5e9 458ncp_conn_findhandle(int connHandle, struct thread *td, struct ncp_handle **handle) {
984263bc
MD
459 struct ncp_handle *refp;
460
dadab5e9 461 lockmgr(&lhlock, LK_SHARED, 0, td);
984263bc 462 SLIST_FOREACH(refp, &lhlist, nh_next)
dadab5e9
MD
463 if (refp->nh_td == td && refp->nh_id == connHandle) break;
464 lockmgr(&lhlock, LK_RELEASE, 0, td);
984263bc
MD
465 if (refp == NULL) {
466 return EBADF;
467 }
468 *handle = refp;
469 return 0;
470}
471/*
472 * Clear handles associated with specified process
473 */
474int
dadab5e9 475ncp_conn_putprochandles(struct thread *td) {
984263bc
MD
476 struct ncp_handle *hp, *nhp;
477 int haveone = 0;
478
dadab5e9 479 lockmgr(&lhlock, LK_EXCLUSIVE, 0, td);
984263bc
MD
480 for (hp = SLIST_FIRST(&lhlist); hp; hp = nhp) {
481 nhp = SLIST_NEXT(hp, nh_next);
dadab5e9 482 if (hp->nh_td != td) continue;
984263bc
MD
483 haveone = 1;
484 hp->nh_conn->ref_cnt -= hp->nh_ref;
485 SLIST_REMOVE(&lhlist, hp, ncp_handle, nh_next);
486 FREE(hp, M_NCPDATA);
487 }
dadab5e9 488 lockmgr(&lhlock, LK_RELEASE, 0, td);
984263bc
MD
489 return haveone;
490}
491/*
492 * remove references in all possible connections,
493 * XXX - possible problem is a locked list.
494 */
495/*void
496ncp_conn_list_rm_ref(pid_t pid) {
497 struct ncp_conn *ncp;
498
499 ncp_conn_locklist(LK_SHARED, NULL);
500 SLIST_FOREACH(ncp, &conn_list, nc_next) {
501 ncp_conn_rm_ref(ncp,pid,1);
502 }
503 ncp_conn_unlocklist(NULL);
504 return;
505}
506*/
507int
508ncp_conn_getinfo(struct ncp_conn *ncp, struct ncp_conn_stat *ncs) {
509 bzero(ncs,sizeof(*ncs));
510 ncs->li = ncp->li;
511 ncs->li.user = ncs->user;
512 if (ncp->li.user)
513 strcpy(ncs->user, ncp->li.user);
514 ncs->li.password = NULL;
515 ncs->connRef = ncp->nc_id;
516 ncs->ref_cnt = ncp->ref_cnt;
517 ncs->connid = ncp->connid;
518 ncs->owner = ncp->nc_owner->cr_uid;
519 ncs->group = ncp->nc_group;
520 ncs->flags = ncp->flags;
521 ncs->buffer_size = ncp->buffer_size;
522 return 0;
523}
524
525static int
526ncp_sysctl_connstat(SYSCTL_HANDLER_ARGS) {
527 int error;
528 struct ncp_conn_stat ncs;
529 struct ncp_conn *ncp;
530/* struct ucred *cred = req->p->p_ucred;*/
531
532 error = 0;
dadab5e9 533 ncp_conn_locklist(LK_SHARED, req->td);
984263bc
MD
534 error = SYSCTL_OUT(req, &ncp_conn_cnt, sizeof(ncp_conn_cnt));
535 SLIST_FOREACH(ncp, &conn_list, nc_next) {
536 if (error) break;
537 /* I can't do conn_lock while list is locked */
538 ncp->nc_lwant++;
539 if (!error) {
540 ncp_conn_getinfo(ncp, &ncs);
541 } else {
542 bzero(&ncs,sizeof(ncs));
543 ncs.connRef = ncp->nc_id;
544 strcpy(ncs.li.server,"***");
545 }
546 ncp->nc_lwant--;
547 error = SYSCTL_OUT(req, &ncs, sizeof(ncs));
548 }
dadab5e9 549 ncp_conn_unlocklist(req->td);
984263bc
MD
550 return(error);
551}