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