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