1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
25 */
26
27 #include <smbsrv/smb_door.h>
28 #include <smbsrv/smb_kproto.h>
29 #include <smbsrv/smb_ktypes.h>
30
31 typedef struct smb_unshare {
32 list_node_t us_lnd;
33 char us_sharename[MAXNAMELEN];
34 } smb_unshare_t;
35
36 static kmem_cache_t *smb_kshare_cache_share;
37 static kmem_cache_t *smb_kshare_cache_unexport;
38 kmem_cache_t *smb_kshare_cache_vfs;
39
40 static int smb_kshare_cmp(const void *, const void *);
41 static void smb_kshare_hold(const void *);
42 static boolean_t smb_kshare_rele(const void *);
43 static void smb_kshare_destroy(void *);
44 static char *smb_kshare_oemname(const char *);
45 static int smb_kshare_is_special(const char *);
46 static boolean_t smb_kshare_is_admin(const char *);
47 static smb_kshare_t *smb_kshare_decode(nvlist_t *);
48 static uint32_t smb_kshare_decode_bool(nvlist_t *, const char *, uint32_t);
49 static void smb_kshare_unexport_thread(smb_thread_t *, void *);
50 static int smb_kshare_export(smb_server_t *, smb_kshare_t *);
51 static int smb_kshare_unexport(smb_server_t *, const char *);
52 static int smb_kshare_export_trans(smb_server_t *, char *, char *, char *);
53 static void smb_kshare_csc_flags(smb_kshare_t *, const char *);
54
55 static boolean_t smb_export_isready(smb_server_t *);
56
57 #ifdef _KERNEL
58 static int smb_kshare_chk_dsrv_status(int, smb_dr_ctx_t *);
59 #endif /* _KERNEL */
60
61 static const smb_avl_nops_t smb_kshare_avlops = {
62 smb_kshare_cmp,
63 smb_kshare_hold,
64 smb_kshare_rele,
65 smb_kshare_destroy
66 };
67
68 #ifdef _KERNEL
69 /*
70 * This function is not MultiThread safe. The caller has to make sure only one
71 * thread calls this function.
72 */
73 door_handle_t
smb_kshare_door_init(int door_id)74 smb_kshare_door_init(int door_id)
75 {
76 return (door_ki_lookup(door_id));
77 }
78
79 /*
80 * This function is not MultiThread safe. The caller has to make sure only one
81 * thread calls this function.
82 */
83 void
smb_kshare_door_fini(door_handle_t dhdl)84 smb_kshare_door_fini(door_handle_t dhdl)
85 {
86 if (dhdl)
87 door_ki_rele(dhdl);
88 }
89
90 /*
91 * This is a special interface that will be utilized by ZFS to cause
92 * a share to be added/removed
93 *
94 * arg is either a smb_share_t or share_name from userspace.
95 * It will need to be copied into the kernel. It is smb_share_t
96 * for add operations and share_name for delete operations.
97 */
98 int
smb_kshare_upcall(door_handle_t dhdl,void * arg,boolean_t add_share)99 smb_kshare_upcall(door_handle_t dhdl, void *arg, boolean_t add_share)
100 {
101 door_arg_t doorarg = { 0 };
102 char *buf = NULL;
103 char *str = NULL;
104 int error;
105 int rc;
106 unsigned int used;
107 smb_dr_ctx_t *dec_ctx;
108 smb_dr_ctx_t *enc_ctx;
109 smb_share_t *lmshare = NULL;
110 int opcode;
111
112 opcode = (add_share) ? SMB_SHROP_ADD : SMB_SHROP_DELETE;
113
114 buf = kmem_alloc(SMB_SHARE_DSIZE, KM_SLEEP);
115 enc_ctx = smb_dr_encode_start(buf, SMB_SHARE_DSIZE);
116 smb_dr_put_uint32(enc_ctx, opcode);
117
118 switch (opcode) {
119 case SMB_SHROP_ADD:
120 lmshare = kmem_alloc(sizeof (smb_share_t), KM_SLEEP);
121 error = xcopyin(arg, lmshare, sizeof (smb_share_t));
122 if (error != 0) {
123 kmem_free(lmshare, sizeof (smb_share_t));
124 kmem_free(buf, SMB_SHARE_DSIZE);
125 return (error);
126 }
127 smb_dr_put_share(enc_ctx, lmshare);
128 break;
129
130 case SMB_SHROP_DELETE:
131 str = kmem_alloc(MAXPATHLEN, KM_SLEEP);
132 error = copyinstr(arg, str, MAXPATHLEN, NULL);
133 if (error != 0) {
134 kmem_free(str, MAXPATHLEN);
135 kmem_free(buf, SMB_SHARE_DSIZE);
136 return (error);
137 }
138 smb_dr_put_string(enc_ctx, str);
139 kmem_free(str, MAXPATHLEN);
140 break;
141 }
142
143 if ((error = smb_dr_encode_finish(enc_ctx, &used)) != 0) {
144 kmem_free(buf, SMB_SHARE_DSIZE);
145 if (lmshare)
146 kmem_free(lmshare, sizeof (smb_share_t));
147 return (NERR_InternalError);
148 }
149
150 doorarg.data_ptr = buf;
151 doorarg.data_size = used;
152 doorarg.rbuf = buf;
153 doorarg.rsize = SMB_SHARE_DSIZE;
154
155 error = door_ki_upcall_limited(dhdl, &doorarg, NULL, SIZE_MAX, 0);
156
157 if (error) {
158 kmem_free(buf, SMB_SHARE_DSIZE);
159 if (lmshare)
160 kmem_free(lmshare, sizeof (smb_share_t));
161 return (error);
162 }
163
164 dec_ctx = smb_dr_decode_start(doorarg.data_ptr, doorarg.data_size);
165 if (smb_kshare_chk_dsrv_status(opcode, dec_ctx) != 0) {
166 kmem_free(buf, SMB_SHARE_DSIZE);
167 if (lmshare)
168 kmem_free(lmshare, sizeof (smb_share_t));
169 return (NERR_InternalError);
170 }
171
172 rc = smb_dr_get_uint32(dec_ctx);
173 if (opcode == SMB_SHROP_ADD)
174 smb_dr_get_share(dec_ctx, lmshare);
175
176 if (smb_dr_decode_finish(dec_ctx))
177 rc = NERR_InternalError;
178
179 kmem_free(buf, SMB_SHARE_DSIZE);
180 if (lmshare)
181 kmem_free(lmshare, sizeof (smb_share_t));
182
183 return ((rc == NERR_DuplicateShare && add_share) ? 0 : rc);
184 }
185 #endif /* _KERNEL */
186
187 /*
188 * Executes map and unmap command for shares.
189 */
190 int
smb_kshare_exec(smb_server_t * sv,smb_shr_execinfo_t * execinfo)191 smb_kshare_exec(smb_server_t *sv, smb_shr_execinfo_t *execinfo)
192 {
193 int exec_rc = 0;
194
195 (void) smb_kdoor_upcall(sv, SMB_DR_SHR_EXEC,
196 execinfo, smb_shr_execinfo_xdr, &exec_rc, xdr_int);
197
198 return (exec_rc);
199 }
200
201 /*
202 * Obtains any host access restriction on the specified
203 * share for the given host (ipaddr) by calling smbd
204 */
205 uint32_t
smb_kshare_hostaccess(smb_kshare_t * shr,smb_session_t * session)206 smb_kshare_hostaccess(smb_kshare_t *shr, smb_session_t *session)
207 {
208 smb_shr_hostaccess_query_t req;
209 smb_inaddr_t *ipaddr = &session->ipaddr;
210 uint32_t host_access = SMB_SHRF_ACC_OPEN;
211 uint32_t flag = SMB_SHRF_ACC_OPEN;
212 uint32_t access;
213
214 if (smb_inet_iszero(ipaddr))
215 return (ACE_ALL_PERMS);
216
217 if ((shr->shr_access_none == NULL || *shr->shr_access_none == '\0') &&
218 (shr->shr_access_ro == NULL || *shr->shr_access_ro == '\0') &&
219 (shr->shr_access_rw == NULL || *shr->shr_access_rw == '\0'))
220 return (ACE_ALL_PERMS);
221
222 if (shr->shr_access_none != NULL)
223 flag |= SMB_SHRF_ACC_NONE;
224 if (shr->shr_access_ro != NULL)
225 flag |= SMB_SHRF_ACC_RO;
226 if (shr->shr_access_rw != NULL)
227 flag |= SMB_SHRF_ACC_RW;
228
229 req.shq_none = shr->shr_access_none;
230 req.shq_ro = shr->shr_access_ro;
231 req.shq_rw = shr->shr_access_rw;
232 req.shq_flag = flag;
233 req.shq_ipaddr = *ipaddr;
234
235 (void) smb_kdoor_upcall(session->s_server, SMB_DR_SHR_HOSTACCESS,
236 &req, smb_shr_hostaccess_query_xdr, &host_access, xdr_uint32_t);
237
238 switch (host_access) {
239 case SMB_SHRF_ACC_RO:
240 access = ACE_ALL_PERMS & ~ACE_ALL_WRITE_PERMS;
241 break;
242 case SMB_SHRF_ACC_OPEN:
243 case SMB_SHRF_ACC_RW:
244 access = ACE_ALL_PERMS;
245 break;
246 case SMB_SHRF_ACC_NONE:
247 default:
248 access = 0;
249 }
250
251 return (access);
252 }
253
254 /*
255 * This function is called when smb_server_t is
256 * created which means smb/service is ready for
257 * exporting SMB shares
258 */
259 void
smb_export_start(smb_server_t * sv)260 smb_export_start(smb_server_t *sv)
261 {
262 mutex_enter(&sv->sv_export.e_mutex);
263 if (sv->sv_export.e_ready) {
264 mutex_exit(&sv->sv_export.e_mutex);
265 return;
266 }
267
268 sv->sv_export.e_ready = B_TRUE;
269 mutex_exit(&sv->sv_export.e_mutex);
270
271 smb_avl_create(&sv->sv_export.e_share_avl, sizeof (smb_kshare_t),
272 offsetof(smb_kshare_t, shr_link), &smb_kshare_avlops);
273
274 (void) smb_kshare_export_trans(sv, "IPC$", "IPC$", "Remote IPC");
275 (void) smb_kshare_export_trans(sv, "c$", SMB_CVOL, "Default Share");
276 (void) smb_kshare_export_trans(sv, "vss$", SMB_VSS, "VSS");
277 }
278
279 /*
280 * This function is called when smb_server_t goes
281 * away which means SMB shares should not be made
282 * available to clients
283 */
284 void
smb_export_stop(smb_server_t * sv)285 smb_export_stop(smb_server_t *sv)
286 {
287 mutex_enter(&sv->sv_export.e_mutex);
288 if (!sv->sv_export.e_ready) {
289 mutex_exit(&sv->sv_export.e_mutex);
290 return;
291 }
292 sv->sv_export.e_ready = B_FALSE;
293 mutex_exit(&sv->sv_export.e_mutex);
294
295 smb_avl_destroy(&sv->sv_export.e_share_avl);
296 smb_vfs_rele_all(&sv->sv_export);
297 }
298
299 void
smb_kshare_g_init(void)300 smb_kshare_g_init(void)
301 {
302 smb_kshare_cache_share = kmem_cache_create("smb_share_cache",
303 sizeof (smb_kshare_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
304
305 smb_kshare_cache_unexport = kmem_cache_create("smb_unexport_cache",
306 sizeof (smb_unshare_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
307
308 smb_kshare_cache_vfs = kmem_cache_create("smb_vfs_cache",
309 sizeof (smb_vfs_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
310 }
311
312 void
smb_kshare_init(smb_server_t * sv)313 smb_kshare_init(smb_server_t *sv)
314 {
315
316 smb_llist_constructor(&sv->sv_export.e_vfs_list, sizeof (smb_vfs_t),
317 offsetof(smb_vfs_t, sv_lnd));
318
319 smb_slist_constructor(&sv->sv_export.e_unexport_list,
320 sizeof (smb_unshare_t), offsetof(smb_unshare_t, us_lnd));
321 }
322
323 int
smb_kshare_start(smb_server_t * sv)324 smb_kshare_start(smb_server_t *sv)
325 {
326 smb_thread_init(&sv->sv_export.e_unexport_thread, "smb_thread_unexport",
327 smb_kshare_unexport_thread, sv, smbsrv_base_pri);
328
329 return (smb_thread_start(&sv->sv_export.e_unexport_thread));
330 }
331
332 void
smb_kshare_stop(smb_server_t * sv)333 smb_kshare_stop(smb_server_t *sv)
334 {
335 smb_thread_stop(&sv->sv_export.e_unexport_thread);
336 smb_thread_destroy(&sv->sv_export.e_unexport_thread);
337 }
338
339 void
smb_kshare_fini(smb_server_t * sv)340 smb_kshare_fini(smb_server_t *sv)
341 {
342 smb_unshare_t *ux;
343
344 while ((ux = list_head(&sv->sv_export.e_unexport_list.sl_list))
345 != NULL) {
346 smb_slist_remove(&sv->sv_export.e_unexport_list, ux);
347 kmem_cache_free(smb_kshare_cache_unexport, ux);
348 }
349 smb_slist_destructor(&sv->sv_export.e_unexport_list);
350
351 smb_vfs_rele_all(&sv->sv_export);
352
353 smb_llist_destructor(&sv->sv_export.e_vfs_list);
354 }
355
356 void
smb_kshare_g_fini(void)357 smb_kshare_g_fini(void)
358 {
359 kmem_cache_destroy(smb_kshare_cache_unexport);
360 kmem_cache_destroy(smb_kshare_cache_share);
361 kmem_cache_destroy(smb_kshare_cache_vfs);
362 }
363
364 /*
365 * A list of shares in nvlist format can be sent down
366 * from userspace thourgh the IOCTL interface. The nvlist
367 * is unpacked here and all the shares in the list will
368 * be exported.
369 */
370 int
smb_kshare_export_list(smb_ioc_share_t * ioc)371 smb_kshare_export_list(smb_ioc_share_t *ioc)
372 {
373 smb_server_t *sv = NULL;
374 nvlist_t *shrlist = NULL;
375 nvlist_t *share;
376 nvpair_t *nvp;
377 smb_kshare_t *shr;
378 char *shrname;
379 int rc;
380
381 if ((rc = smb_server_lookup(&sv)) != 0)
382 return (rc);
383
384 if (!smb_export_isready(sv)) {
385 rc = ENOTACTIVE;
386 goto out;
387 }
388
389 rc = nvlist_unpack(ioc->shr, ioc->shrlen, &shrlist, KM_SLEEP);
390 if (rc != 0)
391 goto out;
392
393 for (nvp = nvlist_next_nvpair(shrlist, NULL); nvp != NULL;
394 nvp = nvlist_next_nvpair(shrlist, nvp)) {
395
396 /*
397 * Since this loop can run for a while we want to exit
398 * as soon as the server state is anything but RUNNING
399 * to allow shutdown to proceed.
400 */
401 if (sv->sv_state != SMB_SERVER_STATE_RUNNING)
402 goto out;
403
404 if (nvpair_type(nvp) != DATA_TYPE_NVLIST)
405 continue;
406
407 shrname = nvpair_name(nvp);
408 ASSERT(shrname);
409
410 if ((rc = nvpair_value_nvlist(nvp, &share)) != 0) {
411 cmn_err(CE_WARN, "export[%s]: failed accessing",
412 shrname);
413 continue;
414 }
415
416 if ((shr = smb_kshare_decode(share)) == NULL) {
417 cmn_err(CE_WARN, "export[%s]: failed decoding",
418 shrname);
419 continue;
420 }
421
422 /* smb_kshare_export consumes shr so it's not leaked */
423 if ((rc = smb_kshare_export(sv, shr)) != 0) {
424 smb_kshare_destroy(shr);
425 continue;
426 }
427 }
428 rc = 0;
429
430 out:
431 nvlist_free(shrlist);
432 smb_server_release(sv);
433 return (rc);
434 }
435
436 /*
437 * This function is invoked when a share is disabled to disconnect trees
438 * and close files. Cleaning up may involve VOP and/or VFS calls, which
439 * may conflict/deadlock with stuck threads if something is amiss with the
440 * file system. Queueing the request for asynchronous processing allows the
441 * call to return immediately so that, if the unshare is being done in the
442 * context of a forced unmount, the forced unmount will always be able to
443 * proceed (unblocking stuck I/O and eventually allowing all blocked unshare
444 * processes to complete).
445 *
446 * The path lookup to find the root vnode of the VFS in question and the
447 * release of this vnode are done synchronously prior to any associated
448 * unmount. Doing these asynchronous to an associated unmount could run
449 * the risk of a spurious EBUSY for a standard unmount or an EIO during
450 * the path lookup due to a forced unmount finishing first.
451 */
452 int
smb_kshare_unexport_list(smb_ioc_share_t * ioc)453 smb_kshare_unexport_list(smb_ioc_share_t *ioc)
454 {
455 smb_server_t *sv = NULL;
456 smb_unshare_t *ux;
457 nvlist_t *shrlist = NULL;
458 nvpair_t *nvp;
459 boolean_t unexport = B_FALSE;
460 char *shrname;
461 int rc;
462
463 if ((rc = smb_server_lookup(&sv)) != 0)
464 return (rc);
465
466 if ((rc = nvlist_unpack(ioc->shr, ioc->shrlen, &shrlist, 0)) != 0)
467 goto out;
468
469 for (nvp = nvlist_next_nvpair(shrlist, NULL); nvp != NULL;
470 nvp = nvlist_next_nvpair(shrlist, nvp)) {
471 if (nvpair_type(nvp) != DATA_TYPE_NVLIST)
472 continue;
473
474 shrname = nvpair_name(nvp);
475 ASSERT(shrname);
476
477 if ((rc = smb_kshare_unexport(sv, shrname)) != 0)
478 continue;
479
480 ux = kmem_cache_alloc(smb_kshare_cache_unexport, KM_SLEEP);
481 (void) strlcpy(ux->us_sharename, shrname, MAXNAMELEN);
482
483 smb_slist_insert_tail(&sv->sv_export.e_unexport_list, ux);
484 unexport = B_TRUE;
485 }
486
487 if (unexport)
488 smb_thread_signal(&sv->sv_export.e_unexport_thread);
489 rc = 0;
490
491 out:
492 nvlist_free(shrlist);
493 smb_server_release(sv);
494 return (rc);
495 }
496
497 /*
498 * Get properties (currently only shortname enablement)
499 * of specified share.
500 */
501 int
smb_kshare_info(smb_ioc_shareinfo_t * ioc)502 smb_kshare_info(smb_ioc_shareinfo_t *ioc)
503 {
504 ioc->shortnames = smb_shortnames;
505 return (0);
506 }
507
508 /*
509 * This function builds a response for a NetShareEnum RAP request.
510 * List of shares is scanned twice. In the first round the total number
511 * of shares which their OEM name is shorter than 13 chars (esi->es_ntotal)
512 * and also the number of shares that fit in the given buffer are calculated.
513 * In the second round the shares data are encoded in the buffer.
514 *
515 * The data associated with each share has two parts, a fixed size part and
516 * a variable size part which is share's comment. The outline of the response
517 * buffer is so that fixed part for all the shares will appear first and follows
518 * with the comments for all those shares and that's why the data cannot be
519 * encoded in one round without unnecessarily complicating the code.
520 */
521 void
smb_kshare_enum(smb_server_t * sv,smb_enumshare_info_t * esi)522 smb_kshare_enum(smb_server_t *sv, smb_enumshare_info_t *esi)
523 {
524 smb_avl_t *share_avl;
525 smb_avl_cursor_t cursor;
526 smb_kshare_t *shr;
527 int remained;
528 uint16_t infolen = 0;
529 uint16_t cmntlen = 0;
530 uint16_t sharelen;
531 uint16_t clen;
532 uint32_t cmnt_offs;
533 smb_msgbuf_t info_mb;
534 smb_msgbuf_t cmnt_mb;
535 boolean_t autohome_added = B_FALSE;
536
537 if (!smb_export_isready(sv)) {
538 esi->es_ntotal = esi->es_nsent = 0;
539 esi->es_datasize = 0;
540 return;
541 }
542
543 esi->es_ntotal = esi->es_nsent = 0;
544 remained = esi->es_bufsize;
545 share_avl = &sv->sv_export.e_share_avl;
546
547 /* Do the necessary calculations in the first round */
548 smb_avl_iterinit(share_avl, &cursor);
549
550 while ((shr = smb_avl_iterate(share_avl, &cursor)) != NULL) {
551 if (shr->shr_oemname == NULL) {
552 smb_avl_release(share_avl, shr);
553 continue;
554 }
555
556 if ((shr->shr_flags & SMB_SHRF_AUTOHOME) && !autohome_added) {
557 if (esi->es_posix_uid == shr->shr_uid) {
558 autohome_added = B_TRUE;
559 } else {
560 smb_avl_release(share_avl, shr);
561 continue;
562 }
563 }
564
565 esi->es_ntotal++;
566
567 if (remained <= 0) {
568 smb_avl_release(share_avl, shr);
569 continue;
570 }
571
572 clen = strlen(shr->shr_cmnt) + 1;
573 sharelen = SHARE_INFO_1_SIZE + clen;
574
575 if (sharelen <= remained) {
576 infolen += SHARE_INFO_1_SIZE;
577 cmntlen += clen;
578 }
579
580 remained -= sharelen;
581 smb_avl_release(share_avl, shr);
582 }
583
584 esi->es_datasize = infolen + cmntlen;
585
586 smb_msgbuf_init(&info_mb, (uint8_t *)esi->es_buf, infolen, 0);
587 smb_msgbuf_init(&cmnt_mb, (uint8_t *)esi->es_buf + infolen, cmntlen, 0);
588 cmnt_offs = infolen;
589
590 /* Encode the data in the second round */
591 smb_avl_iterinit(share_avl, &cursor);
592 autohome_added = B_FALSE;
593
594 while ((shr = smb_avl_iterate(share_avl, &cursor)) != NULL) {
595 if (shr->shr_oemname == NULL) {
596 smb_avl_release(share_avl, shr);
597 continue;
598 }
599
600 if ((shr->shr_flags & SMB_SHRF_AUTOHOME) && !autohome_added) {
601 if (esi->es_posix_uid == shr->shr_uid) {
602 autohome_added = B_TRUE;
603 } else {
604 smb_avl_release(share_avl, shr);
605 continue;
606 }
607 }
608
609 if (smb_msgbuf_encode(&info_mb, "13c.wl",
610 shr->shr_oemname, shr->shr_type, cmnt_offs) < 0) {
611 smb_avl_release(share_avl, shr);
612 break;
613 }
614
615 if (smb_msgbuf_encode(&cmnt_mb, "s", shr->shr_cmnt) < 0) {
616 smb_avl_release(share_avl, shr);
617 break;
618 }
619
620 cmnt_offs += strlen(shr->shr_cmnt) + 1;
621 esi->es_nsent++;
622
623 smb_avl_release(share_avl, shr);
624 }
625
626 smb_msgbuf_term(&info_mb);
627 smb_msgbuf_term(&cmnt_mb);
628 }
629
630 /*
631 * Looks up the given share and returns a pointer
632 * to its definition if it's found. A hold on the
633 * object is taken before the pointer is returned
634 * in which case the caller MUST always call
635 * smb_kshare_release().
636 */
637 smb_kshare_t *
smb_kshare_lookup(smb_server_t * sv,const char * shrname)638 smb_kshare_lookup(smb_server_t *sv, const char *shrname)
639 {
640 smb_kshare_t key;
641 smb_kshare_t *shr;
642
643 ASSERT(shrname);
644
645 if (!smb_export_isready(sv))
646 return (NULL);
647
648 key.shr_name = (char *)shrname;
649 shr = smb_avl_lookup(&sv->sv_export.e_share_avl, &key);
650 return (shr);
651 }
652
653 /*
654 * Releases the hold taken on the specified share object
655 */
656 void
smb_kshare_release(smb_server_t * sv,smb_kshare_t * shr)657 smb_kshare_release(smb_server_t *sv, smb_kshare_t *shr)
658 {
659 ASSERT(shr);
660 ASSERT(shr->shr_magic == SMB_SHARE_MAGIC);
661
662 smb_avl_release(&sv->sv_export.e_share_avl, shr);
663 }
664
665 /*
666 * Add the given share in the specified server.
667 * If the share is a disk share, smb_vfs_hold() is
668 * invoked to ensure that there is a hold on the
669 * corresponding file system before the share is
670 * added to shares AVL.
671 *
672 * If the share is an Autohome share and it is
673 * already in the AVL only a reference count for
674 * that share is incremented.
675 */
676 static int
smb_kshare_export(smb_server_t * sv,smb_kshare_t * shr)677 smb_kshare_export(smb_server_t *sv, smb_kshare_t *shr)
678 {
679 smb_avl_t *share_avl;
680 smb_kshare_t *auto_shr;
681 vnode_t *vp;
682 int rc = 0;
683
684 share_avl = &sv->sv_export.e_share_avl;
685
686 if (!STYPE_ISDSK(shr->shr_type)) {
687 if ((rc = smb_avl_add(share_avl, shr)) != 0) {
688 cmn_err(CE_WARN, "export[%s]: failed caching (%d)",
689 shr->shr_name, rc);
690 }
691
692 return (rc);
693 }
694
695 if ((auto_shr = smb_avl_lookup(share_avl, shr)) != NULL) {
696 if ((auto_shr->shr_flags & SMB_SHRF_AUTOHOME) == 0) {
697 smb_avl_release(share_avl, auto_shr);
698 return (EEXIST);
699 }
700
701 mutex_enter(&auto_shr->shr_mutex);
702 auto_shr->shr_autocnt++;
703 mutex_exit(&auto_shr->shr_mutex);
704 smb_avl_release(share_avl, auto_shr);
705 return (0);
706 }
707
708 if ((rc = smb_server_sharevp(sv, shr->shr_path, &vp)) != 0) {
709 cmn_err(CE_WARN, "export[%s(%s)]: failed obtaining vnode (%d)",
710 shr->shr_name, shr->shr_path, rc);
711 return (rc);
712 }
713
714 if ((rc = smb_vfs_hold(&sv->sv_export, vp->v_vfsp)) == 0) {
715 if ((rc = smb_avl_add(share_avl, shr)) != 0) {
716 cmn_err(CE_WARN, "export[%s]: failed caching (%d)",
717 shr->shr_name, rc);
718 smb_vfs_rele(&sv->sv_export, vp->v_vfsp);
719 }
720 } else {
721 cmn_err(CE_WARN, "export[%s(%s)]: failed holding VFS (%d)",
722 shr->shr_name, shr->shr_path, rc);
723 }
724
725 VN_RELE(vp);
726 return (rc);
727 }
728
729 /*
730 * Removes the share specified by 'shrname' from the AVL
731 * tree of the given server if it's there.
732 *
733 * If the share is an Autohome share, the autohome count
734 * is decremented and the share is only removed if the
735 * count goes to zero.
736 *
737 * If the share is a disk share, the hold on the corresponding
738 * file system is released before removing the share from
739 * the AVL tree.
740 */
741 static int
smb_kshare_unexport(smb_server_t * sv,const char * shrname)742 smb_kshare_unexport(smb_server_t *sv, const char *shrname)
743 {
744 smb_avl_t *share_avl;
745 smb_kshare_t key;
746 smb_kshare_t *shr;
747 vnode_t *vp;
748 int rc;
749 boolean_t auto_unexport;
750
751 share_avl = &sv->sv_export.e_share_avl;
752
753 key.shr_name = (char *)shrname;
754 if ((shr = smb_avl_lookup(share_avl, &key)) == NULL)
755 return (ENOENT);
756
757 if ((shr->shr_flags & SMB_SHRF_AUTOHOME) != 0) {
758 mutex_enter(&shr->shr_mutex);
759 shr->shr_autocnt--;
760 auto_unexport = (shr->shr_autocnt == 0);
761 mutex_exit(&shr->shr_mutex);
762 if (!auto_unexport) {
763 smb_avl_release(share_avl, shr);
764 return (0);
765 }
766 }
767
768 if (STYPE_ISDSK(shr->shr_type)) {
769 if ((rc = smb_server_sharevp(sv, shr->shr_path, &vp)) != 0) {
770 smb_avl_release(share_avl, shr);
771 cmn_err(CE_WARN, "unexport[%s]: failed obtaining vnode"
772 " (%d)", shrname, rc);
773 return (rc);
774 }
775
776 smb_vfs_rele(&sv->sv_export, vp->v_vfsp);
777 VN_RELE(vp);
778 }
779
780 smb_avl_remove(share_avl, shr);
781 smb_avl_release(share_avl, shr);
782
783 return (0);
784 }
785
786 /*
787 * Exports IPC$ or Admin shares
788 */
789 static int
smb_kshare_export_trans(smb_server_t * sv,char * name,char * path,char * cmnt)790 smb_kshare_export_trans(smb_server_t *sv, char *name, char *path, char *cmnt)
791 {
792 smb_kshare_t *shr;
793
794 ASSERT(name);
795 ASSERT(path);
796
797 shr = kmem_cache_alloc(smb_kshare_cache_share, KM_SLEEP);
798 bzero(shr, sizeof (smb_kshare_t));
799
800 shr->shr_magic = SMB_SHARE_MAGIC;
801 shr->shr_refcnt = 1;
802 shr->shr_flags = SMB_SHRF_TRANS | smb_kshare_is_admin(shr->shr_name);
803 if (strcasecmp(name, "IPC$") == 0)
804 shr->shr_type = STYPE_IPC;
805 else
806 shr->shr_type = STYPE_DISKTREE;
807
808 shr->shr_type |= smb_kshare_is_special(shr->shr_name);
809
810 shr->shr_name = smb_mem_strdup(name);
811 if (path)
812 shr->shr_path = smb_mem_strdup(path);
813 if (cmnt)
814 shr->shr_cmnt = smb_mem_strdup(cmnt);
815 shr->shr_oemname = smb_kshare_oemname(name);
816
817 return (smb_kshare_export(sv, shr));
818 }
819
820 /*
821 * Decodes share information in an nvlist format into a smb_kshare_t
822 * structure.
823 *
824 * This is a temporary function and will be replaced by functions
825 * provided by libsharev2 code after it's available.
826 */
827 static smb_kshare_t *
smb_kshare_decode(nvlist_t * share)828 smb_kshare_decode(nvlist_t *share)
829 {
830 smb_kshare_t tmp;
831 smb_kshare_t *shr;
832 nvlist_t *smb;
833 char *csc_name = NULL;
834 int rc;
835
836 ASSERT(share);
837
838 bzero(&tmp, sizeof (smb_kshare_t));
839
840 rc = nvlist_lookup_string(share, "name", &tmp.shr_name);
841 rc |= nvlist_lookup_string(share, "path", &tmp.shr_path);
842 (void) nvlist_lookup_string(share, "desc", &tmp.shr_cmnt);
843
844 ASSERT(tmp.shr_name && tmp.shr_path);
845
846 rc |= nvlist_lookup_nvlist(share, "smb", &smb);
847 if (rc != 0) {
848 cmn_err(CE_WARN, "kshare: failed looking up SMB properties"
849 " (%d)", rc);
850 return (NULL);
851 }
852
853 rc = nvlist_lookup_uint32(smb, "type", &tmp.shr_type);
854 if (rc != 0) {
855 cmn_err(CE_WARN, "kshare[%s]: failed getting the share type"
856 " (%d)", tmp.shr_name, rc);
857 return (NULL);
858 }
859
860 (void) nvlist_lookup_string(smb, SHOPT_AD_CONTAINER,
861 &tmp.shr_container);
862 (void) nvlist_lookup_string(smb, SHOPT_NONE, &tmp.shr_access_none);
863 (void) nvlist_lookup_string(smb, SHOPT_RO, &tmp.shr_access_ro);
864 (void) nvlist_lookup_string(smb, SHOPT_RW, &tmp.shr_access_rw);
865
866 tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_ABE, SMB_SHRF_ABE);
867 tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_CATIA,
868 SMB_SHRF_CATIA);
869 tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_GUEST,
870 SMB_SHRF_GUEST_OK);
871 tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_DFSROOT,
872 SMB_SHRF_DFSROOT);
873 tmp.shr_flags |= smb_kshare_decode_bool(smb, "Autohome",
874 SMB_SHRF_AUTOHOME);
875
876 if ((tmp.shr_flags & SMB_SHRF_AUTOHOME) == SMB_SHRF_AUTOHOME) {
877 rc = nvlist_lookup_uint32(smb, "uid", &tmp.shr_uid);
878 rc |= nvlist_lookup_uint32(smb, "gid", &tmp.shr_gid);
879 if (rc != 0) {
880 cmn_err(CE_WARN, "kshare: failed looking up uid/gid"
881 " (%d)", rc);
882 return (NULL);
883 }
884 }
885
886 (void) nvlist_lookup_string(smb, SHOPT_CSC, &csc_name);
887 smb_kshare_csc_flags(&tmp, csc_name);
888
889 shr = kmem_cache_alloc(smb_kshare_cache_share, KM_SLEEP);
890 bzero(shr, sizeof (smb_kshare_t));
891
892 shr->shr_magic = SMB_SHARE_MAGIC;
893 shr->shr_refcnt = 1;
894
895 shr->shr_name = smb_mem_strdup(tmp.shr_name);
896 shr->shr_path = smb_mem_strdup(tmp.shr_path);
897 if (tmp.shr_cmnt)
898 shr->shr_cmnt = smb_mem_strdup(tmp.shr_cmnt);
899 if (tmp.shr_container)
900 shr->shr_container = smb_mem_strdup(tmp.shr_container);
901 if (tmp.shr_access_none)
902 shr->shr_access_none = smb_mem_strdup(tmp.shr_access_none);
903 if (tmp.shr_access_ro)
904 shr->shr_access_ro = smb_mem_strdup(tmp.shr_access_ro);
905 if (tmp.shr_access_rw)
906 shr->shr_access_rw = smb_mem_strdup(tmp.shr_access_rw);
907
908 shr->shr_oemname = smb_kshare_oemname(shr->shr_name);
909 shr->shr_flags = tmp.shr_flags | smb_kshare_is_admin(shr->shr_name);
910 shr->shr_type = tmp.shr_type | smb_kshare_is_special(shr->shr_name);
911
912 shr->shr_uid = tmp.shr_uid;
913 shr->shr_gid = tmp.shr_gid;
914
915 if ((shr->shr_flags & SMB_SHRF_AUTOHOME) == SMB_SHRF_AUTOHOME)
916 shr->shr_autocnt = 1;
917
918 return (shr);
919 }
920
921 #if 0
922 static void
923 smb_kshare_log(smb_kshare_t *shr)
924 {
925 cmn_err(CE_NOTE, "Share info:");
926 cmn_err(CE_NOTE, "\tname: %s", (shr->shr_name) ? shr->shr_name : "");
927 cmn_err(CE_NOTE, "\tpath: %s", (shr->shr_path) ? shr->shr_path : "");
928 cmn_err(CE_NOTE, "\tcmnt: (%s)",
929 (shr->shr_cmnt) ? shr->shr_cmnt : "NULL");
930 cmn_err(CE_NOTE, "\toemname: (%s)",
931 (shr->shr_oemname) ? shr->shr_oemname : "NULL");
932 cmn_err(CE_NOTE, "\tflags: %X", shr->shr_flags);
933 cmn_err(CE_NOTE, "\ttype: %d", shr->shr_type);
934 }
935 #endif
936
937 /*
938 * Compare function used by shares AVL
939 */
940 static int
smb_kshare_cmp(const void * p1,const void * p2)941 smb_kshare_cmp(const void *p1, const void *p2)
942 {
943 smb_kshare_t *shr1 = (smb_kshare_t *)p1;
944 smb_kshare_t *shr2 = (smb_kshare_t *)p2;
945 int rc;
946
947 ASSERT(shr1);
948 ASSERT(shr1->shr_name);
949
950 ASSERT(shr2);
951 ASSERT(shr2->shr_name);
952
953 rc = smb_strcasecmp(shr1->shr_name, shr2->shr_name, 0);
954
955 if (rc < 0)
956 return (-1);
957
958 if (rc > 0)
959 return (1);
960
961 return (0);
962 }
963
964 /*
965 * This function is called by smb_avl routines whenever
966 * there is a need to take a hold on a share structure
967 * inside AVL
968 */
969 static void
smb_kshare_hold(const void * p)970 smb_kshare_hold(const void *p)
971 {
972 smb_kshare_t *shr = (smb_kshare_t *)p;
973
974 ASSERT(shr);
975 ASSERT(shr->shr_magic == SMB_SHARE_MAGIC);
976
977 mutex_enter(&shr->shr_mutex);
978 shr->shr_refcnt++;
979 mutex_exit(&shr->shr_mutex);
980 }
981
982 /*
983 * This function must be called by smb_avl routines whenever
984 * smb_kshare_hold is called and the hold needs to be released.
985 */
986 static boolean_t
smb_kshare_rele(const void * p)987 smb_kshare_rele(const void *p)
988 {
989 smb_kshare_t *shr = (smb_kshare_t *)p;
990 boolean_t destroy;
991
992 ASSERT(shr);
993 ASSERT(shr->shr_magic == SMB_SHARE_MAGIC);
994
995 mutex_enter(&shr->shr_mutex);
996 ASSERT(shr->shr_refcnt > 0);
997 shr->shr_refcnt--;
998 destroy = (shr->shr_refcnt == 0);
999 mutex_exit(&shr->shr_mutex);
1000
1001 return (destroy);
1002 }
1003
1004 /*
1005 * Frees all the memory allocated for the given
1006 * share structure. It also removes the structure
1007 * from the share cache.
1008 */
1009 static void
smb_kshare_destroy(void * p)1010 smb_kshare_destroy(void *p)
1011 {
1012 smb_kshare_t *shr = (smb_kshare_t *)p;
1013
1014 ASSERT(shr);
1015 ASSERT(shr->shr_magic == SMB_SHARE_MAGIC);
1016
1017 smb_mem_free(shr->shr_name);
1018 smb_mem_free(shr->shr_path);
1019 smb_mem_free(shr->shr_cmnt);
1020 smb_mem_free(shr->shr_container);
1021 smb_mem_free(shr->shr_oemname);
1022 smb_mem_free(shr->shr_access_none);
1023 smb_mem_free(shr->shr_access_ro);
1024 smb_mem_free(shr->shr_access_rw);
1025
1026 kmem_cache_free(smb_kshare_cache_share, shr);
1027 }
1028
1029
1030 /*
1031 * Generate an OEM name for the given share name. If the name is
1032 * shorter than 13 bytes the oemname will be returned; otherwise NULL
1033 * is returned.
1034 */
1035 static char *
smb_kshare_oemname(const char * shrname)1036 smb_kshare_oemname(const char *shrname)
1037 {
1038 smb_wchar_t *unibuf;
1039 char *oem_name;
1040 int length;
1041
1042 length = strlen(shrname) + 1;
1043
1044 oem_name = smb_mem_alloc(length);
1045 unibuf = smb_mem_alloc(length * sizeof (smb_wchar_t));
1046
1047 (void) smb_mbstowcs(unibuf, shrname, length);
1048
1049 if (ucstooem(oem_name, unibuf, length, OEM_CPG_850) == 0)
1050 (void) strcpy(oem_name, shrname);
1051
1052 smb_mem_free(unibuf);
1053
1054 if (strlen(oem_name) + 1 > SMB_SHARE_OEMNAME_MAX) {
1055 smb_mem_free(oem_name);
1056 return (NULL);
1057 }
1058
1059 return (oem_name);
1060 }
1061
1062 /*
1063 * Special share reserved for interprocess communication (IPC$) or
1064 * remote administration of the server (ADMIN$). Can also refer to
1065 * administrative shares such as C$, D$, E$, and so forth.
1066 */
1067 static int
smb_kshare_is_special(const char * sharename)1068 smb_kshare_is_special(const char *sharename)
1069 {
1070 int len;
1071
1072 if (sharename == NULL)
1073 return (0);
1074
1075 if ((len = strlen(sharename)) == 0)
1076 return (0);
1077
1078 if (sharename[len - 1] == '$')
1079 return (STYPE_SPECIAL);
1080
1081 return (0);
1082 }
1083
1084 /*
1085 * Check whether or not this is a default admin share: C$, D$ etc.
1086 */
1087 static boolean_t
smb_kshare_is_admin(const char * sharename)1088 smb_kshare_is_admin(const char *sharename)
1089 {
1090 if (sharename == NULL)
1091 return (B_FALSE);
1092
1093 if (strlen(sharename) == 2 &&
1094 smb_isalpha(sharename[0]) && sharename[1] == '$') {
1095 return (B_TRUE);
1096 }
1097
1098 return (B_FALSE);
1099 }
1100
1101 /*
1102 * Decodes the given boolean share option.
1103 * If the option is present in the nvlist and it's value is true
1104 * returns the corresponding flag value, otherwise returns 0.
1105 */
1106 static uint32_t
smb_kshare_decode_bool(nvlist_t * nvl,const char * propname,uint32_t flag)1107 smb_kshare_decode_bool(nvlist_t *nvl, const char *propname, uint32_t flag)
1108 {
1109 char *boolp;
1110
1111 if (nvlist_lookup_string(nvl, propname, &boolp) == 0)
1112 if (strcasecmp(boolp, "true") == 0)
1113 return (flag);
1114
1115 return (0);
1116 }
1117
1118 /*
1119 * Map a client-side caching (CSC) option to the appropriate share
1120 * flag. Only one option is allowed; an error will be logged if
1121 * multiple options have been specified. We don't need to do anything
1122 * about multiple values here because the SRVSVC will not recognize
1123 * a value containing multiple flags and will return the default value.
1124 *
1125 * If the option value is not recognized, it will be ignored: invalid
1126 * values will typically be caught and rejected by sharemgr.
1127 */
1128 static void
smb_kshare_csc_flags(smb_kshare_t * shr,const char * value)1129 smb_kshare_csc_flags(smb_kshare_t *shr, const char *value)
1130 {
1131 int i;
1132 static struct {
1133 char *value;
1134 uint32_t flag;
1135 } cscopt[] = {
1136 { "disabled", SMB_SHRF_CSC_DISABLED },
1137 { "manual", SMB_SHRF_CSC_MANUAL },
1138 { "auto", SMB_SHRF_CSC_AUTO },
1139 { "vdo", SMB_SHRF_CSC_VDO }
1140 };
1141
1142 if (value == NULL)
1143 return;
1144
1145 for (i = 0; i < (sizeof (cscopt) / sizeof (cscopt[0])); ++i) {
1146 if (strcasecmp(value, cscopt[i].value) == 0) {
1147 shr->shr_flags |= cscopt[i].flag;
1148 break;
1149 }
1150 }
1151
1152 switch (shr->shr_flags & SMB_SHRF_CSC_MASK) {
1153 case 0:
1154 case SMB_SHRF_CSC_DISABLED:
1155 case SMB_SHRF_CSC_MANUAL:
1156 case SMB_SHRF_CSC_AUTO:
1157 case SMB_SHRF_CSC_VDO:
1158 break;
1159
1160 default:
1161 cmn_err(CE_NOTE, "csc option conflict: 0x%08x",
1162 shr->shr_flags & SMB_SHRF_CSC_MASK);
1163 break;
1164 }
1165 }
1166
1167 /*
1168 * This function processes the unexport event list and disconnects shares
1169 * asynchronously. The function executes as a zone-specific thread.
1170 *
1171 * The server arg passed in is safe to use without a reference count, because
1172 * the server cannot be deleted until smb_thread_stop()/destroy() return,
1173 * which is also when the thread exits.
1174 */
1175 /*ARGSUSED*/
1176 static void
smb_kshare_unexport_thread(smb_thread_t * thread,void * arg)1177 smb_kshare_unexport_thread(smb_thread_t *thread, void *arg)
1178 {
1179 smb_server_t *sv = arg;
1180 smb_unshare_t *ux;
1181
1182 while (smb_thread_continue(thread)) {
1183 while ((ux = list_head(&sv->sv_export.e_unexport_list.sl_list))
1184 != NULL) {
1185 smb_slist_remove(&sv->sv_export.e_unexport_list, ux);
1186 (void) smb_server_unshare(ux->us_sharename);
1187 kmem_cache_free(smb_kshare_cache_unexport, ux);
1188 }
1189 }
1190 }
1191
1192 static boolean_t
smb_export_isready(smb_server_t * sv)1193 smb_export_isready(smb_server_t *sv)
1194 {
1195 boolean_t ready;
1196
1197 mutex_enter(&sv->sv_export.e_mutex);
1198 ready = sv->sv_export.e_ready;
1199 mutex_exit(&sv->sv_export.e_mutex);
1200
1201 return (ready);
1202 }
1203
1204 #ifdef _KERNEL
1205 /*
1206 * Return 0 upon success. Otherwise > 0
1207 */
1208 static int
smb_kshare_chk_dsrv_status(int opcode,smb_dr_ctx_t * dec_ctx)1209 smb_kshare_chk_dsrv_status(int opcode, smb_dr_ctx_t *dec_ctx)
1210 {
1211 int status = smb_dr_get_int32(dec_ctx);
1212 int err;
1213
1214 switch (status) {
1215 case SMB_SHARE_DSUCCESS:
1216 return (0);
1217
1218 case SMB_SHARE_DERROR:
1219 err = smb_dr_get_uint32(dec_ctx);
1220 cmn_err(CE_WARN, "%d: Encountered door server error %d",
1221 opcode, err);
1222 (void) smb_dr_decode_finish(dec_ctx);
1223 return (err);
1224 }
1225
1226 ASSERT(0);
1227 return (EINVAL);
1228 }
1229 #endif /* _KERNEL */
1230