]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netsmb/smb_conn.c
Implement pci_enable_msi() and pci_disable_msi() in the LinuxKPI.
[FreeBSD/FreeBSD.git] / sys / netsmb / smb_conn.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2000-2001 Boris Popov
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 /*
30  * Connection engine.
31  */
32
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/malloc.h>
40 #include <sys/priv.h>
41 #include <sys/proc.h>
42 #include <sys/lock.h>
43 #include <sys/sysctl.h>
44 #include <sys/socketvar.h>
45
46 #include <sys/iconv.h>
47
48 #include <netsmb/smb.h>
49 #include <netsmb/smb_subr.h>
50 #include <netsmb/smb_conn.h>
51 #include <netsmb/smb_tran.h>
52 #include <netsmb/smb_trantcp.h>
53
54 static struct smb_connobj smb_vclist;
55 static int smb_vcnext = 1;      /* next unique id for VC */
56
57 SYSCTL_NODE(_net, OID_AUTO, smb, CTLFLAG_RW, NULL, "SMB protocol");
58
59 static MALLOC_DEFINE(M_SMBCONN, "smb_conn", "SMB connection");
60
61 static void smb_co_init(struct smb_connobj *cp, int level, char *ilockname,
62     char *lockname);
63 static void smb_co_done(struct smb_connobj *cp);
64 static int  smb_vc_disconnect(struct smb_vc *vcp);
65 static void smb_vc_free(struct smb_connobj *cp);
66 static void smb_vc_gone(struct smb_connobj *cp, struct smb_cred *scred);
67 static smb_co_free_t smb_share_free;
68 static smb_co_gone_t smb_share_gone;
69
70 static int  smb_sysctl_treedump(SYSCTL_HANDLER_ARGS);
71
72 SYSCTL_PROC(_net_smb, OID_AUTO, treedump, CTLFLAG_RD | CTLTYPE_OPAQUE,
73             NULL, 0, smb_sysctl_treedump, "S,treedump", "Requester tree");
74
75 int
76 smb_sm_init(void)
77 {
78
79         smb_co_init(&smb_vclist, SMBL_SM, "smbsm ilock", "smbsm");
80         sx_xlock(&smb_vclist.co_interlock);
81         smb_co_unlock(&smb_vclist);
82         sx_unlock(&smb_vclist.co_interlock);
83         return 0;
84 }
85
86 int
87 smb_sm_done(void)
88 {
89
90         /* XXX: hold the mutex */
91         if (smb_vclist.co_usecount > 1) {
92                 SMBERROR("%d connections still active\n", smb_vclist.co_usecount - 1);
93                 return EBUSY;
94         }
95         smb_co_done(&smb_vclist);
96         return 0;
97 }
98
99 static int
100 smb_sm_lockvclist(void)
101 {
102         int error;
103
104         sx_xlock(&smb_vclist.co_interlock);
105         error = smb_co_lock(&smb_vclist);
106         sx_unlock(&smb_vclist.co_interlock);
107
108         return error;
109 }
110
111 static void
112 smb_sm_unlockvclist(void)
113 {
114
115         sx_xlock(&smb_vclist.co_interlock);
116         smb_co_unlock(&smb_vclist);
117         sx_unlock(&smb_vclist.co_interlock);
118 }
119
120 static int
121 smb_sm_lookupint(struct smb_vcspec *vcspec, struct smb_sharespec *shspec,
122         struct smb_cred *scred, struct smb_vc **vcpp)
123 {
124         struct smb_connobj *scp;
125         struct smb_vc *vcp;
126         int exact = 1;
127         int error;
128
129         vcspec->shspec = shspec;
130         error = ENOENT;
131         vcp = NULL;
132         SMBCO_FOREACH(scp, &smb_vclist) {
133                 vcp = (struct smb_vc *)scp;
134                 error = smb_vc_lock(vcp);
135                 if (error)
136                         continue;
137
138                 if ((vcp->obj.co_flags & SMBV_PRIVATE) ||
139                     !CONNADDREQ(vcp->vc_paddr, vcspec->sap) ||
140                     strcmp(vcp->vc_username, vcspec->username) != 0)
141                         goto err1;
142                 if (vcspec->owner != SMBM_ANY_OWNER) {
143                         if (vcp->vc_uid != vcspec->owner)
144                                 goto err1;
145                 } else
146                         exact = 0;
147                 if (vcspec->group != SMBM_ANY_GROUP) {
148                         if (vcp->vc_grp != vcspec->group)
149                                 goto err1;
150                 } else
151                         exact = 0;
152                 if (vcspec->mode & SMBM_EXACT) {
153                         if (!exact || (vcspec->mode & SMBM_MASK) !=
154                             vcp->vc_mode)
155                                 goto err1;
156                 }
157                 if (smb_vc_access(vcp, scred, vcspec->mode) != 0)
158                         goto err1;
159                 vcspec->ssp = NULL;
160                 if (shspec) {
161                         error = (int)smb_vc_lookupshare(vcp, shspec, scred,
162                             &vcspec->ssp);
163                         if (error)
164                                 goto fail;
165                 }
166                 error = 0;
167                 break;
168         err1:
169                 error = 1;
170         fail:
171                 smb_vc_unlock(vcp);
172         }
173         if (vcp) {
174                 smb_vc_ref(vcp);
175                 *vcpp = vcp;
176         }
177         return (error);
178 }
179
180 int
181 smb_sm_lookup(struct smb_vcspec *vcspec, struct smb_sharespec *shspec,
182         struct smb_cred *scred, struct smb_vc **vcpp)
183 {
184         struct smb_vc *vcp;
185         struct smb_share *ssp = NULL;
186         int error;
187
188         *vcpp = vcp = NULL;
189
190         error = smb_sm_lockvclist();
191         if (error)
192                 return error;
193         error = smb_sm_lookupint(vcspec, shspec, scred, vcpp);
194         if (error == 0 || (vcspec->flags & SMBV_CREATE) == 0) {
195                 smb_sm_unlockvclist();
196                 return error;
197         }
198         error = smb_sm_lookupint(vcspec, NULL, scred, &vcp);
199         if (error) {
200                 error = smb_vc_create(vcspec, scred, &vcp);
201                 if (error)
202                         goto out;
203                 error = smb_vc_connect(vcp, scred);
204                 if (error)
205                         goto out;
206         }
207         if (shspec == NULL)
208                 goto out;
209         error = smb_share_create(vcp, shspec, scred, &ssp);
210         if (error)
211                 goto out;
212         error = smb_smb_treeconnect(ssp, scred);
213         if (error == 0)
214                 vcspec->ssp = ssp;
215         else
216                 smb_share_put(ssp, scred);
217 out:
218         smb_sm_unlockvclist();
219         if (error == 0)
220                 *vcpp = vcp;
221         else if (vcp) {
222                 smb_vc_lock(vcp);
223                 smb_vc_put(vcp, scred);
224         }
225         return error;
226 }
227
228 /*
229  * Common code for connection object
230  */
231 static void
232 smb_co_init(struct smb_connobj *cp, int level, char *ilockname, char *lockname)
233 {
234         SLIST_INIT(&cp->co_children);
235         sx_init_flags(&cp->co_interlock, ilockname, SX_RECURSE);
236         cv_init(&cp->co_lock, "smblock");
237         cp->co_lockcnt = 0;
238         cp->co_locker = NULL;
239         cp->co_level = level;
240         cp->co_usecount = 1;
241         sx_xlock(&cp->co_interlock);
242         smb_co_lock(cp);
243         sx_unlock(&cp->co_interlock);
244 }
245
246 static void
247 smb_co_done(struct smb_connobj *cp)
248 {
249
250         sx_destroy(&cp->co_interlock);
251         cv_destroy(&cp->co_lock);
252         cp->co_locker = NULL;
253         cp->co_flags = 0;
254         cp->co_lockcnt = 0;
255 }
256
257 static void
258 smb_co_gone(struct smb_connobj *cp, struct smb_cred *scred)
259 {
260         struct smb_connobj *parent;
261
262         if (cp->co_gone)
263                 cp->co_gone(cp, scred);
264         parent = cp->co_parent;
265         if (parent) {
266                 sx_xlock(&parent->co_interlock);
267                 smb_co_lock(parent);
268                 sx_unlock(&parent->co_interlock);
269                 SLIST_REMOVE(&parent->co_children, cp, smb_connobj, co_next);
270                 smb_co_put(parent, scred);
271         }
272         if (cp->co_free)
273                 cp->co_free(cp);
274 }
275
276 void
277 smb_co_ref(struct smb_connobj *cp)
278 {
279
280         sx_xlock(&cp->co_interlock);
281         cp->co_usecount++;
282         sx_unlock(&cp->co_interlock);
283 }
284
285 void
286 smb_co_rele(struct smb_connobj *cp, struct smb_cred *scred)
287 {
288
289         sx_xlock(&cp->co_interlock);
290         smb_co_unlock(cp);
291         if (cp->co_usecount > 1) {
292                 cp->co_usecount--;
293                 sx_unlock(&cp->co_interlock);
294                 return;
295         }
296         if (cp->co_usecount == 0) {
297                 SMBERROR("negative use_count for object %d", cp->co_level);
298                 sx_unlock(&cp->co_interlock);
299                 return;
300         }
301         cp->co_usecount--;
302         cp->co_flags |= SMBO_GONE;
303         sx_unlock(&cp->co_interlock);
304         smb_co_gone(cp, scred);
305 }
306
307 int
308 smb_co_get(struct smb_connobj *cp, struct smb_cred *scred)
309 {
310         int error;
311
312         MPASS(sx_xholder(&cp->co_interlock) == curthread);
313         cp->co_usecount++;
314         error = smb_co_lock(cp);
315         if (error) 
316                 cp->co_usecount--;
317         return error;
318 }
319
320 void
321 smb_co_put(struct smb_connobj *cp, struct smb_cred *scred)
322 {
323
324         sx_xlock(&cp->co_interlock);
325         if (cp->co_usecount > 1) {
326                 cp->co_usecount--;
327         } else if (cp->co_usecount == 1) {
328                 cp->co_usecount--;
329                 cp->co_flags |= SMBO_GONE;
330         } else {
331                 SMBERROR("negative usecount");
332         }
333         smb_co_unlock(cp);
334         sx_unlock(&cp->co_interlock);
335         if ((cp->co_flags & SMBO_GONE) == 0)
336                 return;
337         smb_co_gone(cp, scred);
338 }
339
340 int
341 smb_co_lock(struct smb_connobj *cp)
342 {
343
344         MPASS(sx_xholder(&cp->co_interlock) == curthread); 
345         for (;;) {
346                 if (cp->co_flags & SMBO_GONE)
347                         return EINVAL;
348                 if (cp->co_locker == NULL) {
349                         cp->co_locker = curthread;
350                         return 0;
351                 }
352                 if (cp->co_locker == curthread) {
353                         cp->co_lockcnt++;
354                         return 0;
355                 }
356                 cv_wait(&cp->co_lock, &cp->co_interlock);
357         }
358 }
359
360 void
361 smb_co_unlock(struct smb_connobj *cp)
362 {
363
364         MPASS(sx_xholder(&cp->co_interlock) == curthread); 
365         MPASS(cp->co_locker == curthread);
366         if (cp->co_lockcnt != 0) {
367                 cp->co_lockcnt--;
368                 return;
369         }
370         cp->co_locker = NULL;
371         cv_signal(&cp->co_lock);
372 }
373
374 static void
375 smb_co_addchild(struct smb_connobj *parent, struct smb_connobj *child)
376 {
377
378         smb_co_ref(parent);
379         SLIST_INSERT_HEAD(&parent->co_children, child, co_next);
380         child->co_parent = parent;
381 }
382
383 /*
384  * Session implementation
385  */
386
387 int
388 smb_vc_create(struct smb_vcspec *vcspec,
389         struct smb_cred *scred, struct smb_vc **vcpp)
390 {
391         struct smb_vc *vcp;
392         struct ucred *cred = scred->scr_cred;
393         uid_t uid = vcspec->owner;
394         gid_t gid = vcspec->group;
395         uid_t realuid = cred->cr_uid;
396         char *domain = vcspec->domain;
397         int error, isroot;
398
399         isroot = smb_suser(cred) == 0;
400         /*
401          * Only superuser can create VCs with different uid and gid
402          */
403         if (uid != SMBM_ANY_OWNER && uid != realuid && !isroot)
404                 return EPERM;
405         if (gid != SMBM_ANY_GROUP && !groupmember(gid, cred) && !isroot)
406                 return EPERM;
407
408         vcp = smb_zmalloc(sizeof(*vcp), M_SMBCONN, M_WAITOK);
409         smb_co_init(VCTOCP(vcp), SMBL_VC, "smb_vc ilock", "smb_vc");
410         vcp->obj.co_free = smb_vc_free;
411         vcp->obj.co_gone = smb_vc_gone;
412         vcp->vc_number = smb_vcnext++;
413         vcp->vc_timo = SMB_DEFRQTIMO;
414         vcp->vc_smbuid = SMB_UID_UNKNOWN;
415         vcp->vc_mode = vcspec->rights & SMBM_MASK;
416         vcp->obj.co_flags = vcspec->flags & (SMBV_PRIVATE | SMBV_SINGLESHARE);
417         vcp->vc_tdesc = &smb_tran_nbtcp_desc;
418         vcp->vc_seqno = 0;
419         vcp->vc_mackey = NULL;
420         vcp->vc_mackeylen = 0;
421
422         if (uid == SMBM_ANY_OWNER)
423                 uid = realuid;
424         if (gid == SMBM_ANY_GROUP)
425                 gid = cred->cr_groups[0];
426         vcp->vc_uid = uid;
427         vcp->vc_grp = gid;
428
429         smb_sl_init(&vcp->vc_stlock, "vcstlock");
430         error = ENOMEM;
431
432         vcp->vc_paddr = sodupsockaddr(vcspec->sap, M_WAITOK);
433         if (vcp->vc_paddr == NULL)
434                 goto fail;
435         vcp->vc_laddr = sodupsockaddr(vcspec->lap, M_WAITOK);
436         if (vcp->vc_laddr == NULL)
437                 goto fail;
438         vcp->vc_pass = smb_strdup(vcspec->pass);
439         if (vcp->vc_pass == NULL)
440                 goto fail;
441         vcp->vc_domain = smb_strdup((domain && domain[0]) ? domain :
442             "NODOMAIN");
443         if (vcp->vc_domain == NULL)
444                 goto fail;
445         vcp->vc_srvname = smb_strdup(vcspec->srvname);
446         if (vcp->vc_srvname == NULL)
447                 goto fail;
448         vcp->vc_username = smb_strdup(vcspec->username);
449         if (vcp->vc_username == NULL)
450                 goto fail;
451         error = (int)iconv_open("tolower", vcspec->localcs, &vcp->vc_tolower);
452         if (error)
453                 goto fail;
454         error = (int)iconv_open("toupper", vcspec->localcs, &vcp->vc_toupper);
455         if (error)
456                 goto fail;
457         if (vcspec->servercs[0]) {
458                 error = (int)iconv_open(vcspec->servercs, vcspec->localcs,
459                     &vcp->vc_cp_toserver);
460                 if (error)
461                         goto fail;
462                 error = (int)iconv_open(vcspec->localcs, vcspec->servercs,
463                     &vcp->vc_cp_tolocal);
464                 if (error)
465                         goto fail;
466                 vcp->vc_toserver = vcp->vc_cp_toserver;
467                 vcp->vc_tolocal = vcp->vc_cp_tolocal;
468                 iconv_add(ENCODING_UNICODE, ENCODING_UNICODE, SMB_UNICODE_NAME);
469                 iconv_add(ENCODING_UNICODE, SMB_UNICODE_NAME, ENCODING_UNICODE);
470                 error = (int)iconv_open(SMB_UNICODE_NAME, vcspec->localcs,
471                     &vcp->vc_ucs_toserver);
472                 if (!error) {
473                         error = (int)iconv_open(vcspec->localcs, SMB_UNICODE_NAME,
474                             &vcp->vc_ucs_tolocal);
475                 }
476                 if (error) {
477                         if (vcp->vc_ucs_toserver)
478                                 iconv_close(vcp->vc_ucs_toserver);
479                         vcp->vc_ucs_toserver = NULL;
480                         vcp->vc_ucs_tolocal = NULL;
481                 }
482         }
483         error = (int)smb_iod_create(vcp);
484         if (error)
485                 goto fail;
486         *vcpp = vcp;
487         smb_co_addchild(&smb_vclist, VCTOCP(vcp));
488         return (0);
489
490  fail:
491         smb_vc_put(vcp, scred);
492         return (error);
493 }
494
495 static void
496 smb_vc_free(struct smb_connobj *cp)
497 {
498         struct smb_vc *vcp = CPTOVC(cp);
499
500         if (vcp->vc_iod)
501                 smb_iod_destroy(vcp->vc_iod);
502         SMB_STRFREE(vcp->vc_username);
503         SMB_STRFREE(vcp->vc_srvname);
504         SMB_STRFREE(vcp->vc_pass);
505         SMB_STRFREE(vcp->vc_domain);
506         if (vcp->vc_mackey)
507                 free(vcp->vc_mackey, M_SMBTEMP);
508         if (vcp->vc_paddr)
509                 free(vcp->vc_paddr, M_SONAME);
510         if (vcp->vc_laddr)
511                 free(vcp->vc_laddr, M_SONAME);
512         if (vcp->vc_tolower)
513                 iconv_close(vcp->vc_tolower);
514         if (vcp->vc_toupper)
515                 iconv_close(vcp->vc_toupper);
516         if (vcp->vc_tolocal)
517                 vcp->vc_tolocal = NULL;
518         if (vcp->vc_toserver)
519                 vcp->vc_toserver = NULL;
520         if (vcp->vc_cp_tolocal)
521                 iconv_close(vcp->vc_cp_tolocal);
522         if (vcp->vc_cp_toserver)
523                 iconv_close(vcp->vc_cp_toserver);
524         if (vcp->vc_ucs_tolocal)
525                 iconv_close(vcp->vc_ucs_tolocal);
526         if (vcp->vc_ucs_toserver)
527                 iconv_close(vcp->vc_ucs_toserver);
528         smb_co_done(VCTOCP(vcp));
529         smb_sl_destroy(&vcp->vc_stlock);
530         free(vcp, M_SMBCONN);
531 }
532
533 /*
534  * Called when use count of VC dropped to zero.
535  */
536 static void
537 smb_vc_gone(struct smb_connobj *cp, struct smb_cred *scred)
538 {
539         struct smb_vc *vcp = CPTOVC(cp);
540
541         smb_vc_disconnect(vcp);
542 }
543
544 void
545 smb_vc_ref(struct smb_vc *vcp)
546 {
547         smb_co_ref(VCTOCP(vcp));
548 }
549
550 void
551 smb_vc_rele(struct smb_vc *vcp, struct smb_cred *scred)
552 {
553         smb_co_rele(VCTOCP(vcp), scred);
554 }
555
556 int
557 smb_vc_get(struct smb_vc *vcp, struct smb_cred *scred)
558 {
559         struct smb_connobj *cp;
560         int error;
561
562         cp = VCTOCP(vcp);
563         sx_xlock(&cp->co_interlock);
564         error = smb_co_get(cp, scred);
565         sx_unlock(&cp->co_interlock);
566         return error;
567 }
568
569 void
570 smb_vc_put(struct smb_vc *vcp, struct smb_cred *scred)
571 {
572         smb_co_put(VCTOCP(vcp), scred);
573 }
574
575 int
576 smb_vc_lock(struct smb_vc *vcp)
577 {
578         struct smb_connobj *cp;
579         int error;
580
581         cp = VCTOCP(vcp);
582         sx_xlock(&cp->co_interlock);
583         error = smb_co_lock(cp);
584         sx_unlock(&cp->co_interlock);
585         return error;
586 }
587
588 void
589 smb_vc_unlock(struct smb_vc *vcp)
590 {
591
592         struct smb_connobj *cp;
593
594         cp = VCTOCP(vcp);
595         sx_xlock(&cp->co_interlock);
596         smb_co_unlock(cp);
597         sx_unlock(&cp->co_interlock);
598 }
599
600 int
601 smb_vc_access(struct smb_vc *vcp, struct smb_cred *scred, mode_t mode)
602 {
603         struct ucred *cred = scred->scr_cred;
604
605         if (smb_suser(cred) == 0 || cred->cr_uid == vcp->vc_uid)
606                 return 0;
607         mode >>= 3;
608         if (!groupmember(vcp->vc_grp, cred))
609                 mode >>= 3;
610         return (vcp->vc_mode & mode) == mode ? 0 : EACCES;
611 }
612
613 static int
614 smb_vc_cmpshare(struct smb_share *ssp, struct smb_sharespec *dp)
615 {
616         int exact = 1;
617
618         if (strcmp(ssp->ss_name, dp->name) != 0)
619                 return 1;
620         if (dp->owner != SMBM_ANY_OWNER) {
621                 if (ssp->ss_uid != dp->owner)
622                         return 1;
623         } else
624                 exact = 0;
625         if (dp->group != SMBM_ANY_GROUP) {
626                 if (ssp->ss_grp != dp->group)
627                         return 1;
628         } else
629                 exact = 0;
630
631         if (dp->mode & SMBM_EXACT) {
632                 if (!exact)
633                         return 1;
634                 return (dp->mode & SMBM_MASK) == ssp->ss_mode ? 0 : 1;
635         }
636         if (smb_share_access(ssp, dp->scred, dp->mode) != 0)
637                 return 1;
638         return 0;
639 }
640
641 /*
642  * Lookup share in the given VC. Share referenced and locked on return.
643  * VC expected to be locked on entry and will be left locked on exit.
644  */
645 int
646 smb_vc_lookupshare(struct smb_vc *vcp, struct smb_sharespec *dp,
647         struct smb_cred *scred, struct smb_share **sspp)
648 {
649         struct smb_connobj *scp = NULL;
650         struct smb_share *ssp = NULL;
651         int error;
652
653         *sspp = NULL;
654         dp->scred = scred;
655         SMBCO_FOREACH(scp, VCTOCP(vcp)) {
656                 ssp = (struct smb_share *)scp;
657                 error = smb_share_lock(ssp);
658                 if (error)
659                         continue;
660                 if (smb_vc_cmpshare(ssp, dp) == 0)
661                         break;
662                 smb_share_unlock(ssp);
663         }
664         if (ssp) {
665                 smb_share_ref(ssp);
666                 *sspp = ssp;
667                 error = 0;
668         } else
669                 error = ENOENT;
670         return error;
671 }
672
673 int
674 smb_vc_connect(struct smb_vc *vcp, struct smb_cred *scred)
675 {
676
677         return smb_iod_request(vcp->vc_iod, SMBIOD_EV_CONNECT | SMBIOD_EV_SYNC, NULL);
678 }
679
680 /*
681  * Destroy VC to server, invalidate shares linked with it.
682  * Transport should be locked on entry.
683  */
684 int
685 smb_vc_disconnect(struct smb_vc *vcp)
686 {
687
688         if (vcp->vc_iod != NULL)
689                 smb_iod_request(vcp->vc_iod, SMBIOD_EV_DISCONNECT |
690                     SMBIOD_EV_SYNC, NULL);
691         return 0;
692 }
693
694 static char smb_emptypass[] = "";
695
696 const char *
697 smb_vc_getpass(struct smb_vc *vcp)
698 {
699         if (vcp->vc_pass)
700                 return vcp->vc_pass;
701         return smb_emptypass;
702 }
703
704 static int
705 smb_vc_getinfo(struct smb_vc *vcp, struct smb_vc_info *vip)
706 {
707         bzero(vip, sizeof(struct smb_vc_info));
708         vip->itype = SMB_INFO_VC;
709         vip->usecount = vcp->obj.co_usecount;
710         vip->uid = vcp->vc_uid;
711         vip->gid = vcp->vc_grp;
712         vip->mode = vcp->vc_mode;
713         vip->flags = vcp->obj.co_flags;
714         vip->sopt = vcp->vc_sopt;
715         vip->iodstate = vcp->vc_iod->iod_state;
716         bzero(&vip->sopt.sv_skey, sizeof(vip->sopt.sv_skey));
717         snprintf(vip->srvname, sizeof(vip->srvname), "%s", vcp->vc_srvname);
718         snprintf(vip->vcname, sizeof(vip->vcname), "%s", vcp->vc_username);
719         return 0;
720 }
721
722 u_short
723 smb_vc_nextmid(struct smb_vc *vcp)
724 {
725         u_short r;
726         
727         sx_xlock(&vcp->obj.co_interlock);
728         r = vcp->vc_mid++;
729         sx_unlock(&vcp->obj.co_interlock);
730         return r;
731 }
732
733 /*
734  * Share implementation
735  */
736 /*
737  * Allocate share structure and attach it to the given VC
738  * Connection expected to be locked on entry. Share will be returned
739  * in locked state.
740  */
741 int
742 smb_share_create(struct smb_vc *vcp, struct smb_sharespec *shspec,
743         struct smb_cred *scred, struct smb_share **sspp)
744 {
745         struct smb_share *ssp;
746         struct ucred *cred = scred->scr_cred;
747         uid_t realuid = cred->cr_uid;
748         uid_t uid = shspec->owner;
749         gid_t gid = shspec->group;
750         int error, isroot;
751
752         isroot = smb_suser(cred) == 0;
753         /*
754          * Only superuser can create shares with different uid and gid
755          */
756         if (uid != SMBM_ANY_OWNER && uid != realuid && !isroot)
757                 return EPERM;
758         if (gid != SMBM_ANY_GROUP && !groupmember(gid, cred) && !isroot)
759                 return EPERM;
760         error = smb_vc_lookupshare(vcp, shspec, scred, &ssp);
761         if (!error) {
762                 smb_share_put(ssp, scred);
763                 return EEXIST;
764         }
765         if (uid == SMBM_ANY_OWNER)
766                 uid = realuid;
767         if (gid == SMBM_ANY_GROUP)
768                 gid = cred->cr_groups[0];
769         ssp = smb_zmalloc(sizeof(*ssp), M_SMBCONN, M_WAITOK);
770         smb_co_init(SSTOCP(ssp), SMBL_SHARE, "smbss ilock", "smbss");
771         ssp->obj.co_free = smb_share_free;
772         ssp->obj.co_gone = smb_share_gone;
773         smb_sl_init(&ssp->ss_stlock, "ssstlock");
774         ssp->ss_name = smb_strdup(shspec->name);
775         if (shspec->pass && shspec->pass[0])
776                 ssp->ss_pass = smb_strdup(shspec->pass);
777         ssp->ss_type = shspec->stype;
778         ssp->ss_tid = SMB_TID_UNKNOWN;
779         ssp->ss_uid = uid;
780         ssp->ss_grp = gid;
781         ssp->ss_mode = shspec->rights & SMBM_MASK;
782         smb_co_addchild(VCTOCP(vcp), SSTOCP(ssp));
783         *sspp = ssp;
784         return 0;
785 }
786
787 static void
788 smb_share_free(struct smb_connobj *cp)
789 {
790         struct smb_share *ssp = CPTOSS(cp);
791
792         SMB_STRFREE(ssp->ss_name);
793         SMB_STRFREE(ssp->ss_pass);
794         smb_sl_destroy(&ssp->ss_stlock);
795         smb_co_done(SSTOCP(ssp));
796         free(ssp, M_SMBCONN);
797 }
798
799 static void
800 smb_share_gone(struct smb_connobj *cp, struct smb_cred *scred)
801 {
802         struct smb_share *ssp = CPTOSS(cp);
803
804         smb_smb_treedisconnect(ssp, scred);
805 }
806
807 void
808 smb_share_ref(struct smb_share *ssp)
809 {
810         smb_co_ref(SSTOCP(ssp));
811 }
812
813 void
814 smb_share_rele(struct smb_share *ssp, struct smb_cred *scred)
815 {
816         smb_co_rele(SSTOCP(ssp), scred);
817 }
818
819 int
820 smb_share_get(struct smb_share *ssp, struct smb_cred *scred)
821 {
822         struct smb_connobj *cp = SSTOCP(ssp);
823         int error;
824
825         sx_xlock(&cp->co_interlock);
826         error = smb_co_get(cp, scred);
827         sx_unlock(&cp->co_interlock);
828         return error;
829 }
830
831 void
832 smb_share_put(struct smb_share *ssp, struct smb_cred *scred)
833 {
834         
835         smb_co_put(SSTOCP(ssp), scred);
836 }
837
838 int
839 smb_share_lock(struct smb_share *ssp)
840 {
841         struct smb_connobj *cp;
842         int error;
843         
844         cp = SSTOCP(ssp);
845         sx_xlock(&cp->co_interlock);
846         error = smb_co_lock(cp);
847         sx_unlock(&cp->co_interlock);
848         return error;
849 }
850
851 void
852 smb_share_unlock(struct smb_share *ssp)
853 {
854         struct smb_connobj *cp;
855         
856         cp = SSTOCP(ssp);
857         sx_xlock(&cp->co_interlock);
858         smb_co_unlock(cp);
859         sx_unlock(&cp->co_interlock);
860 }
861
862 int
863 smb_share_access(struct smb_share *ssp, struct smb_cred *scred, mode_t mode)
864 {
865         struct ucred *cred = scred->scr_cred;
866
867         if (smb_suser(cred) == 0 || cred->cr_uid == ssp->ss_uid)
868                 return 0;
869         mode >>= 3;
870         if (!groupmember(ssp->ss_grp, cred))
871                 mode >>= 3;
872         return (ssp->ss_mode & mode) == mode ? 0 : EACCES;
873 }
874
875 void
876 smb_share_invalidate(struct smb_share *ssp)
877 {
878         ssp->ss_tid = SMB_TID_UNKNOWN;
879 }
880
881 int
882 smb_share_valid(struct smb_share *ssp)
883 {
884         return ssp->ss_tid != SMB_TID_UNKNOWN &&
885             ssp->ss_vcgenid == SSTOVC(ssp)->vc_genid;
886 }
887
888 const char*
889 smb_share_getpass(struct smb_share *ssp)
890 {
891         struct smb_vc *vcp;
892
893         if (ssp->ss_pass)
894                 return ssp->ss_pass;
895         vcp = SSTOVC(ssp);
896         if (vcp->vc_pass)
897                 return vcp->vc_pass;
898         return smb_emptypass;
899 }
900
901 static int
902 smb_share_getinfo(struct smb_share *ssp, struct smb_share_info *sip)
903 {
904         bzero(sip, sizeof(struct smb_share_info));
905         sip->itype = SMB_INFO_SHARE;
906         sip->usecount = ssp->obj.co_usecount;
907         sip->tid  = ssp->ss_tid;
908         sip->type= ssp->ss_type;
909         sip->uid = ssp->ss_uid;
910         sip->gid = ssp->ss_grp;
911         sip->mode= ssp->ss_mode;
912         sip->flags = ssp->obj.co_flags;
913         snprintf(sip->sname, sizeof(sip->sname), "%s", ssp->ss_name);
914         return 0;
915 }
916
917 /*
918  * Dump an entire tree into sysctl call
919  */
920 static int
921 smb_sysctl_treedump(SYSCTL_HANDLER_ARGS)
922 {
923         struct smb_connobj *scp1, *scp2;
924         struct smb_vc *vcp;
925         struct smb_share *ssp;
926         struct smb_vc_info vci;
927         struct smb_share_info ssi;
928         int error, itype;
929
930         error = sysctl_wire_old_buffer(req, 0);
931         if (error)
932                 return (error);
933         error = smb_sm_lockvclist();
934         if (error)
935                 return error;
936         SMBCO_FOREACH(scp1, &smb_vclist) {
937                 vcp = (struct smb_vc *)scp1;
938                 error = smb_vc_lock(vcp);
939                 if (error)
940                         continue;
941                 smb_vc_getinfo(vcp, &vci);
942                 error = SYSCTL_OUT(req, &vci, sizeof(struct smb_vc_info));
943                 if (error) {
944                         smb_vc_unlock(vcp);
945                         break;
946                 }
947                 SMBCO_FOREACH(scp2, VCTOCP(vcp)) {
948                         ssp = (struct smb_share *)scp2;
949                         error = smb_share_lock(ssp);
950                         if (error) {
951                                 error = 0;
952                                 continue;
953                         }
954                         smb_share_getinfo(ssp, &ssi);
955                         smb_share_unlock(ssp);
956                         error = SYSCTL_OUT(req, &ssi, sizeof(struct smb_share_info));
957                         if (error)
958                                 break;
959                 }
960                 smb_vc_unlock(vcp);
961                 if (error)
962                         break;
963         }
964         if (!error) {
965                 itype = SMB_INFO_NONE;
966                 error = SYSCTL_OUT(req, &itype, sizeof(itype));
967         }
968         smb_sm_unlockvclist();
969         return error;
970 }