linprocfs - Introduce /proc/mounts
[dragonfly.git] / sys / netproto / ncp / ncp_conn.c
blobd96473e4fcd6972d07085a7500bf06e70197beb9
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.15 2007/05/13 18:33:58 swildner 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/priv.h>
42 #include <sys/lock.h>
43 #include <sys/sysctl.h>
45 #include "ncp.h"
46 #include "ncp_subr.h"
47 #include "ncp_conn.h"
49 SLIST_HEAD(ncp_handle_head,ncp_handle);
51 int ncp_burst_enabled = 1;
53 struct ncp_conn_head conn_list={NULL};
54 static int ncp_conn_cnt = 0;
55 static int ncp_next_ref = 1;
56 static struct lock listlock;
58 struct ncp_handle_head lhlist={NULL};
59 static int ncp_next_handle = 1;
60 static struct lock lhlock;
62 static int ncp_sysctl_connstat(SYSCTL_HANDLER_ARGS);
63 static int ncp_conn_lock_any(struct ncp_conn *conn, struct thread *td,
64 struct ucred *cred);
66 SYSCTL_DECL(_net_ncp);
67 SYSCTL_INT (_net_ncp, OID_AUTO, burst_enabled, CTLFLAG_RD, &ncp_burst_enabled, 0, "");
68 SYSCTL_INT (_net_ncp, OID_AUTO, conn_cnt, CTLFLAG_RD, &ncp_conn_cnt, 0, "");
69 SYSCTL_PROC(_net_ncp, OID_AUTO, conn_stat, CTLFLAG_RD|CTLTYPE_OPAQUE,
70 NULL, 0, ncp_sysctl_connstat, "S,connstat", "Connections list");
72 MALLOC_DEFINE(M_NCPDATA, "NCP data", "NCP private data");
74 int
75 ncp_conn_init(void) {
76 lockinit(&listlock, "ncpll", 0, 0);
77 lockinit(&lhlock, "ncplh", 0, 0);
78 return 0;
81 int
82 ncp_conn_locklist(int flags, struct thread *td)
84 return lockmgr(&listlock, flags | LK_CANRECURSE);
87 void
88 ncp_conn_unlocklist(struct thread *td)
90 lockmgr(&listlock, LK_RELEASE);
93 int
94 ncp_conn_access(struct ncp_conn *conn, struct ucred *cred, mode_t mode)
96 int error;
98 if (cred == NOCRED || ncp_suser(cred) == 0 ||
99 cred->cr_uid == conn->nc_owner->cr_uid)
100 return 0;
101 mode >>= 3;
102 if (!groupmember(conn->nc_group, cred))
103 mode >>= 3;
104 error = (conn->li.access_mode & mode) == mode ? 0 : EACCES;
105 return error;
109 * NOTE: The lockmgr() call is protected by nc_lwant
112 ncp_conn_lock_any(struct ncp_conn *conn, struct thread *td, struct ucred *cred)
114 int error;
116 if (conn->nc_id == 0)
117 return EACCES;
118 error = lockmgr(&conn->nc_lock, LK_EXCLUSIVE | LK_CANRECURSE);
119 if (error == ERESTART)
120 return EINTR;
121 error = ncp_chkintr(conn, td);
122 if (error) {
123 lockmgr(&conn->nc_lock, LK_RELEASE);
124 return error;
127 if (conn->nc_id == 0) {
128 lockmgr(&conn->nc_lock, LK_RELEASE);
129 return EACCES;
131 conn->td = td; /* who currently operates */
132 conn->ucred = cred;
133 return 0;
137 * NOTE: The lockmgr() call should be protected by nc_lwant, but it
138 * is unclear whether callers have coded this correctly.
141 ncp_conn_lock(struct ncp_conn *conn, struct thread *td,
142 struct ucred *cred, int mode)
144 int error;
146 error = ncp_conn_access(conn, cred, mode);
147 if (error)
148 return error;
149 return ncp_conn_lock_any(conn, td, cred);
153 * Lock conn but unlock connlist
155 static int
156 ncp_conn_lock2(struct ncp_conn *conn, struct thread *td,
157 struct ucred *cred, int mode)
159 int error;
161 error = ncp_conn_access(conn, cred, mode);
162 if (error) {
163 ncp_conn_unlocklist(td);
164 return error;
166 conn->nc_lwant++;
167 ncp_conn_unlocklist(td);
168 error = ncp_conn_lock_any(conn, td, cred);
169 conn->nc_lwant--;
170 if (conn->nc_lwant == 0) {
171 wakeup(&conn->nc_lwant);
173 return error;
176 void
177 ncp_conn_unlock(struct ncp_conn *conn, struct thread *td) {
179 * note, that LK_RELASE will do wakeup() instead of wakeup_one().
180 * this will do a little overhead
182 lockmgr(&conn->nc_lock, LK_RELEASE);
185 int
186 ncp_conn_assert_locked(struct ncp_conn *conn, const char *checker,
187 struct thread *td)
189 if (conn->nc_lock.lk_flags & LK_HAVE_EXCL)
190 return 0;
191 kprintf("%s: connection isn't locked!\n", checker);
192 return EIO;
196 * create, fill with defaults and return in locked state
199 ncp_conn_alloc(struct thread *td, struct ucred *cred, struct ncp_conn **conn)
201 int error;
202 struct ncp_conn *ncp;
204 MALLOC(ncp, struct ncp_conn *, sizeof(struct ncp_conn),
205 M_NCPDATA, M_WAITOK | M_ZERO);
206 error = 0;
207 lockinit(&ncp->nc_lock, "ncplck", 0, 0);
208 ncp_conn_cnt++;
209 ncp->nc_id = ncp_next_ref++;
210 ncp->nc_owner = cred;
211 ncp->seq = 0;
212 ncp->connid = 0xFFFF;
213 ncp_conn_lock_any(ncp, td, ncp->nc_owner);
214 *conn = ncp;
215 ncp_conn_locklist(LK_EXCLUSIVE, td);
216 SLIST_INSERT_HEAD(&conn_list,ncp,nc_next);
217 ncp_conn_unlocklist(td);
218 return (error);
222 * Remove the connection, on entry it must be locked
225 ncp_conn_free(struct ncp_conn *ncp)
227 int error;
228 struct ncp_conn *ncp1;
230 if (ncp == NULL) {
231 NCPFATAL("conn==NULL !\n");
232 return(EIO);
234 if (ncp->nc_id == 0) {
235 kprintf("already!!!!\n");
236 return EACCES;
238 error = ncp_conn_assert_locked(ncp, __func__, ncp->td);
239 if (error)
240 return error;
241 if (ncp->ref_cnt) {
242 NCPFATAL("there are %d references left\n",ncp->ref_cnt);
243 return(EBUSY);
247 * Mark conn as died and wait for other process. This also
248 * interlocks against other ncp_conn_free() ops.
250 ncp->nc_id = 0;
251 while (ncp->nc_lwant) {
252 ncp_conn_unlock(ncp, ncp->td);
253 kprintf("lwant = %d\n", ncp->nc_lwant);
254 tsleep(&ncp->nc_lwant, 0, "ncpdr", 2*hz);
255 lockmgr(&ncp->nc_lock, LK_EXCLUSIVE);
257 ncp_conn_locklist(LK_EXCLUSIVE, ncp->td);
258 SLIST_FOREACH(ncp1, &conn_list, nc_next) {
259 if (ncp1 == ncp)
260 break;
262 KKASSERT(ncp1 == ncp);
263 SLIST_REMOVE(&conn_list, ncp, ncp_conn, nc_next);
264 ncp_conn_cnt--;
265 ncp_conn_unlocklist(ncp->td);
266 if (ncp->li.user) kfree(ncp->li.user, M_NCPDATA);
267 if (ncp->li.password) kfree(ncp->li.password, M_NCPDATA);
268 crfree(ncp->nc_owner);
269 FREE(ncp, M_NCPDATA);
270 return (0);
274 * Lookup connection by handle, return a locked conn descriptor
277 ncp_conn_getbyref(int ref,struct thread *td,struct ucred *cred, int mode, struct ncp_conn **connpp){
278 struct ncp_conn *ncp;
279 int error=0;
281 ncp_conn_locklist(LK_SHARED, td);
282 SLIST_FOREACH(ncp, &conn_list, nc_next)
283 if (ncp->nc_id == ref) break;
284 if (ncp == NULL) {
285 ncp_conn_unlocklist(td);
286 return(EBADF);
288 error = ncp_conn_lock2(ncp, td, cred, mode);
289 if (!error)
290 *connpp = ncp;
291 return (error);
294 * find attached, but not logged in connection to specified server
297 ncp_conn_getattached(struct ncp_conn_args *li,struct thread *td,struct ucred *cred,int mode, struct ncp_conn **connpp){
298 struct ncp_conn *ncp, *ncp2=NULL;
299 int error = 0;
301 ncp_conn_locklist(LK_SHARED, td);
302 SLIST_FOREACH(ncp, &conn_list, nc_next) {
303 if ((ncp->flags & NCPFL_LOGGED) != 0 ||
304 strcmp(ncp->li.server,li->server) != 0 ||
305 ncp->li.saddr.sa_len != li->saddr.sa_len ||
306 bcmp(&ncp->li.saddr,&ncp->li.saddr,li->saddr.sa_len) != 0)
307 continue;
308 if (ncp_suser(cred) == 0 ||
309 cred->cr_uid == ncp->nc_owner->cr_uid)
310 break;
311 error = ncp_conn_access(ncp,cred,mode);
312 if (!error && ncp2 == NULL)
313 ncp2 = ncp;
315 if (ncp == NULL) ncp = ncp2;
316 if (ncp == NULL) {
317 ncp_conn_unlocklist(td);
318 return(EBADF);
320 error = ncp_conn_lock2(ncp,td,cred,mode);
321 if (!error)
322 *connpp=ncp;
323 return (error);
327 * Lookup connection by server/user pair, return a locked conn descriptor.
328 * if li is NULL or server/user pair incomplete, try to select best connection
329 * based on owner.
330 * Connection selected in next order:
331 * 1. Try to search conn with ucred owner, if li is NULL also find a primary
332 * 2. If 1. fails try to get first suitable shared connection
333 * 3. If 2. fails then nothing can help to poor ucred owner
337 ncp_conn_getbyli(struct ncp_conn_args *li, struct thread *td,
338 struct ucred *cred, int mode, struct ncp_conn **connpp)
340 struct ncp_conn *ncp, *ncp2=NULL;
341 int error=0, partial, haveserv;
343 partial = (li == NULL || li->server[0] == 0 || li->user == NULL);
344 haveserv = (li && li->server[0]);
345 ncp_conn_locklist(LK_SHARED, td);
346 SLIST_FOREACH(ncp, &conn_list, nc_next) {
347 if (partial) {
348 if (cred->cr_uid == ncp->nc_owner->cr_uid) {
349 if (haveserv) {
350 if (strcmp(ncp->li.server,li->server) == 0)
351 break;
352 } else {
353 if (ncp->flags & NCPFL_PRIMARY)
354 break;
355 ncp2 = ncp;
357 continue;
359 } else {
360 if (strcmp(ncp->li.server,li->server) != 0 ||
361 ncp->li.user == NULL ||
362 strcmp(ncp->li.user,li->user) != 0)
363 continue;
364 if (cred->cr_uid == ncp->nc_owner->cr_uid)
365 break;
366 if (ncp_suser(cred) == 0)
367 ncp2 = ncp;
369 error = ncp_conn_access(ncp,cred,mode);
370 if (!error && ncp2 == NULL)
371 ncp2 = ncp;
373 if (ncp == NULL) ncp = ncp2;
374 if (ncp == NULL) {
375 ncp_conn_unlocklist(td);
376 return(EBADF);
378 error = ncp_conn_lock2(ncp,td,cred,mode);
379 if (!error)
380 *connpp=ncp;
381 return (error);
385 * Set primary connection flag, since it have sence only for an owner,
386 * only owner can modify this flag.
387 * connection expected to be locked.
390 ncp_conn_setprimary(struct ncp_conn *conn, int on){
391 struct ncp_conn *ncp=NULL;
393 if (conn->ucred->cr_uid != conn->nc_owner->cr_uid)
394 return EACCES;
395 ncp_conn_locklist(LK_SHARED, conn->td);
396 SLIST_FOREACH(ncp, &conn_list, nc_next) {
397 if (conn->ucred->cr_uid == ncp->nc_owner->cr_uid)
398 ncp->flags &= ~NCPFL_PRIMARY;
400 ncp_conn_unlocklist(conn->td);
401 if (on)
402 conn->flags |= NCPFL_PRIMARY;
403 return 0;
406 * Lease conn to given proc, returning unique handle
407 * problem: how locks should be applied ?
410 ncp_conn_gethandle(struct ncp_conn *conn, struct thread *td, struct ncp_handle **handle){
411 struct ncp_handle *refp;
413 lockmgr(&lhlock, LK_EXCLUSIVE);
414 SLIST_FOREACH(refp, &lhlist, nh_next)
415 if (refp->nh_conn == conn && td == refp->nh_td) break;
416 if (refp) {
417 conn->ref_cnt++;
418 refp->nh_ref++;
419 *handle = refp;
420 lockmgr(&lhlock, LK_RELEASE);
421 return 0;
423 MALLOC(refp,struct ncp_handle *,sizeof(struct ncp_handle),M_NCPDATA,
424 M_WAITOK | M_ZERO);
425 SLIST_INSERT_HEAD(&lhlist,refp,nh_next);
426 refp->nh_ref++;
427 refp->nh_td = td;
428 refp->nh_conn = conn;
429 refp->nh_id = ncp_next_handle++;
430 *handle = refp;
431 conn->ref_cnt++;
432 lockmgr(&lhlock, LK_RELEASE);
433 return 0;
436 * release reference, if force - ignore refcount
439 ncp_conn_puthandle(struct ncp_handle *handle, struct thread *td, int force) {
440 struct ncp_handle *refp = handle;
442 lockmgr(&lhlock, LK_EXCLUSIVE);
443 refp->nh_ref--;
444 refp->nh_conn->ref_cnt--;
445 if (force) {
446 refp->nh_conn->ref_cnt -= refp->nh_ref;
447 refp->nh_ref = 0;
449 if (refp->nh_ref == 0) {
450 SLIST_REMOVE(&lhlist, refp, ncp_handle, nh_next);
451 FREE(refp, M_NCPDATA);
453 lockmgr(&lhlock, LK_RELEASE);
454 return 0;
457 * find a connHandle
460 ncp_conn_findhandle(int connHandle, struct thread *td, struct ncp_handle **handle) {
461 struct ncp_handle *refp;
463 lockmgr(&lhlock, LK_SHARED);
464 SLIST_FOREACH(refp, &lhlist, nh_next)
465 if (refp->nh_td == td && refp->nh_id == connHandle) break;
466 lockmgr(&lhlock, LK_RELEASE);
467 if (refp == NULL) {
468 return EBADF;
470 *handle = refp;
471 return 0;
474 * Clear handles associated with specified process
477 ncp_conn_putprochandles(struct thread *td) {
478 struct ncp_handle *hp, *nhp;
479 int haveone = 0;
481 lockmgr(&lhlock, LK_EXCLUSIVE);
482 for (hp = SLIST_FIRST(&lhlist); hp; hp = nhp) {
483 nhp = SLIST_NEXT(hp, nh_next);
484 if (hp->nh_td != td) continue;
485 haveone = 1;
486 hp->nh_conn->ref_cnt -= hp->nh_ref;
487 SLIST_REMOVE(&lhlist, hp, ncp_handle, nh_next);
488 FREE(hp, M_NCPDATA);
490 lockmgr(&lhlock, LK_RELEASE);
491 return haveone;
494 * remove references in all possible connections,
495 * XXX - possible problem is a locked list.
497 /*void
498 ncp_conn_list_rm_ref(pid_t pid) {
499 struct ncp_conn *ncp;
501 ncp_conn_locklist(LK_SHARED, NULL);
502 SLIST_FOREACH(ncp, &conn_list, nc_next) {
503 ncp_conn_rm_ref(ncp,pid,1);
505 ncp_conn_unlocklist(NULL);
506 return;
510 ncp_conn_getinfo(struct ncp_conn *ncp, struct ncp_conn_stat *ncs) {
511 bzero(ncs,sizeof(*ncs));
512 ncs->li = ncp->li;
513 ncs->li.user = ncs->user;
514 if (ncp->li.user)
515 strcpy(ncs->user, ncp->li.user);
516 ncs->li.password = NULL;
517 ncs->connRef = ncp->nc_id;
518 ncs->ref_cnt = ncp->ref_cnt;
519 ncs->connid = ncp->connid;
520 ncs->owner = ncp->nc_owner->cr_uid;
521 ncs->group = ncp->nc_group;
522 ncs->flags = ncp->flags;
523 ncs->buffer_size = ncp->buffer_size;
524 return 0;
527 static int
528 ncp_sysctl_connstat(SYSCTL_HANDLER_ARGS) {
529 int error;
530 struct ncp_conn_stat ncs;
531 struct ncp_conn *ncp;
532 /* struct ucred *cred = req->p->p_ucred;*/
534 error = 0;
535 ncp_conn_locklist(LK_SHARED, req->td);
536 error = SYSCTL_OUT(req, &ncp_conn_cnt, sizeof(ncp_conn_cnt));
537 SLIST_FOREACH(ncp, &conn_list, nc_next) {
538 if (error) break;
539 /* I can't do conn_lock while list is locked */
540 ncp->nc_lwant++;
541 if (!error) {
542 ncp_conn_getinfo(ncp, &ncs);
543 } else {
544 bzero(&ncs,sizeof(ncs));
545 ncs.connRef = ncp->nc_id;
546 strcpy(ncs.li.server,"***");
548 ncp->nc_lwant--;
549 error = SYSCTL_OUT(req, &ncs, sizeof(ncs));
551 ncp_conn_unlocklist(req->td);
552 return(error);