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