xref: /titanic_41/usr/src/uts/common/fs/smbsrv/smb_kshare.c (revision 7206bf49b1fe641544165ee97f63856da95e0868)
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 	if (shrlist != NULL)
432 		nvlist_free(shrlist);
433 	smb_server_release(sv);
434 	return (rc);
435 }
436 
437 /*
438  * This function is invoked when a share is disabled to disconnect trees
439  * and close files.  Cleaning up may involve VOP and/or VFS calls, which
440  * may conflict/deadlock with stuck threads if something is amiss with the
441  * file system.  Queueing the request for asynchronous processing allows the
442  * call to return immediately so that, if the unshare is being done in the
443  * context of a forced unmount, the forced unmount will always be able to
444  * proceed (unblocking stuck I/O and eventually allowing all blocked unshare
445  * processes to complete).
446  *
447  * The path lookup to find the root vnode of the VFS in question and the
448  * release of this vnode are done synchronously prior to any associated
449  * unmount.  Doing these asynchronous to an associated unmount could run
450  * the risk of a spurious EBUSY for a standard unmount or an EIO during
451  * the path lookup due to a forced unmount finishing first.
452  */
453 int
smb_kshare_unexport_list(smb_ioc_share_t * ioc)454 smb_kshare_unexport_list(smb_ioc_share_t *ioc)
455 {
456 	smb_server_t	*sv = NULL;
457 	smb_unshare_t	*ux;
458 	nvlist_t	*shrlist = NULL;
459 	nvpair_t	*nvp;
460 	boolean_t	unexport = B_FALSE;
461 	char		*shrname;
462 	int		rc;
463 
464 	if ((rc = smb_server_lookup(&sv)) != 0)
465 		return (rc);
466 
467 	if ((rc = nvlist_unpack(ioc->shr, ioc->shrlen, &shrlist, 0)) != 0)
468 		goto out;
469 
470 	for (nvp = nvlist_next_nvpair(shrlist, NULL); nvp != NULL;
471 	    nvp = nvlist_next_nvpair(shrlist, nvp)) {
472 		if (nvpair_type(nvp) != DATA_TYPE_NVLIST)
473 			continue;
474 
475 		shrname = nvpair_name(nvp);
476 		ASSERT(shrname);
477 
478 		if ((rc = smb_kshare_unexport(sv, shrname)) != 0)
479 			continue;
480 
481 		ux = kmem_cache_alloc(smb_kshare_cache_unexport, KM_SLEEP);
482 		(void) strlcpy(ux->us_sharename, shrname, MAXNAMELEN);
483 
484 		smb_slist_insert_tail(&sv->sv_export.e_unexport_list, ux);
485 		unexport = B_TRUE;
486 	}
487 
488 	if (unexport)
489 		smb_thread_signal(&sv->sv_export.e_unexport_thread);
490 	rc = 0;
491 
492 out:
493 	if (shrlist != NULL)
494 		nvlist_free(shrlist);
495 	smb_server_release(sv);
496 	return (rc);
497 }
498 
499 /*
500  * Get properties (currently only shortname enablement)
501  * of specified share.
502  */
503 int
smb_kshare_info(smb_ioc_shareinfo_t * ioc)504 smb_kshare_info(smb_ioc_shareinfo_t *ioc)
505 {
506 	ioc->shortnames = smb_shortnames;
507 	return (0);
508 }
509 
510 /*
511  * This function builds a response for a NetShareEnum RAP request.
512  * List of shares is scanned twice. In the first round the total number
513  * of shares which their OEM name is shorter than 13 chars (esi->es_ntotal)
514  * and also the number of shares that fit in the given buffer are calculated.
515  * In the second round the shares data are encoded in the buffer.
516  *
517  * The data associated with each share has two parts, a fixed size part and
518  * a variable size part which is share's comment. The outline of the response
519  * buffer is so that fixed part for all the shares will appear first and follows
520  * with the comments for all those shares and that's why the data cannot be
521  * encoded in one round without unnecessarily complicating the code.
522  */
523 void
smb_kshare_enum(smb_server_t * sv,smb_enumshare_info_t * esi)524 smb_kshare_enum(smb_server_t *sv, smb_enumshare_info_t *esi)
525 {
526 	smb_avl_t *share_avl;
527 	smb_avl_cursor_t cursor;
528 	smb_kshare_t *shr;
529 	int remained;
530 	uint16_t infolen = 0;
531 	uint16_t cmntlen = 0;
532 	uint16_t sharelen;
533 	uint16_t clen;
534 	uint32_t cmnt_offs;
535 	smb_msgbuf_t info_mb;
536 	smb_msgbuf_t cmnt_mb;
537 	boolean_t autohome_added = B_FALSE;
538 
539 	if (!smb_export_isready(sv)) {
540 		esi->es_ntotal = esi->es_nsent = 0;
541 		esi->es_datasize = 0;
542 		return;
543 	}
544 
545 	esi->es_ntotal = esi->es_nsent = 0;
546 	remained = esi->es_bufsize;
547 	share_avl = &sv->sv_export.e_share_avl;
548 
549 	/* Do the necessary calculations in the first round */
550 	smb_avl_iterinit(share_avl, &cursor);
551 
552 	while ((shr = smb_avl_iterate(share_avl, &cursor)) != NULL) {
553 		if (shr->shr_oemname == NULL) {
554 			smb_avl_release(share_avl, shr);
555 			continue;
556 		}
557 
558 		if ((shr->shr_flags & SMB_SHRF_AUTOHOME) && !autohome_added) {
559 			if (esi->es_posix_uid == shr->shr_uid) {
560 				autohome_added = B_TRUE;
561 			} else {
562 				smb_avl_release(share_avl, shr);
563 				continue;
564 			}
565 		}
566 
567 		esi->es_ntotal++;
568 
569 		if (remained <= 0) {
570 			smb_avl_release(share_avl, shr);
571 			continue;
572 		}
573 
574 		clen = strlen(shr->shr_cmnt) + 1;
575 		sharelen = SHARE_INFO_1_SIZE + clen;
576 
577 		if (sharelen <= remained) {
578 			infolen += SHARE_INFO_1_SIZE;
579 			cmntlen += clen;
580 		}
581 
582 		remained -= sharelen;
583 		smb_avl_release(share_avl, shr);
584 	}
585 
586 	esi->es_datasize = infolen + cmntlen;
587 
588 	smb_msgbuf_init(&info_mb, (uint8_t *)esi->es_buf, infolen, 0);
589 	smb_msgbuf_init(&cmnt_mb, (uint8_t *)esi->es_buf + infolen, cmntlen, 0);
590 	cmnt_offs = infolen;
591 
592 	/* Encode the data in the second round */
593 	smb_avl_iterinit(share_avl, &cursor);
594 	autohome_added = B_FALSE;
595 
596 	while ((shr = smb_avl_iterate(share_avl, &cursor)) != NULL) {
597 		if (shr->shr_oemname == NULL) {
598 			smb_avl_release(share_avl, shr);
599 			continue;
600 		}
601 
602 		if ((shr->shr_flags & SMB_SHRF_AUTOHOME) && !autohome_added) {
603 			if (esi->es_posix_uid == shr->shr_uid) {
604 				autohome_added = B_TRUE;
605 			} else {
606 				smb_avl_release(share_avl, shr);
607 				continue;
608 			}
609 		}
610 
611 		if (smb_msgbuf_encode(&info_mb, "13c.wl",
612 		    shr->shr_oemname, shr->shr_type, cmnt_offs) < 0) {
613 			smb_avl_release(share_avl, shr);
614 			break;
615 		}
616 
617 		if (smb_msgbuf_encode(&cmnt_mb, "s", shr->shr_cmnt) < 0) {
618 			smb_avl_release(share_avl, shr);
619 			break;
620 		}
621 
622 		cmnt_offs += strlen(shr->shr_cmnt) + 1;
623 		esi->es_nsent++;
624 
625 		smb_avl_release(share_avl, shr);
626 	}
627 
628 	smb_msgbuf_term(&info_mb);
629 	smb_msgbuf_term(&cmnt_mb);
630 }
631 
632 /*
633  * Looks up the given share and returns a pointer
634  * to its definition if it's found. A hold on the
635  * object is taken before the pointer is returned
636  * in which case the caller MUST always call
637  * smb_kshare_release().
638  */
639 smb_kshare_t *
smb_kshare_lookup(smb_server_t * sv,const char * shrname)640 smb_kshare_lookup(smb_server_t *sv, const char *shrname)
641 {
642 	smb_kshare_t key;
643 	smb_kshare_t *shr;
644 
645 	ASSERT(shrname);
646 
647 	if (!smb_export_isready(sv))
648 		return (NULL);
649 
650 	key.shr_name = (char *)shrname;
651 	shr = smb_avl_lookup(&sv->sv_export.e_share_avl, &key);
652 	return (shr);
653 }
654 
655 /*
656  * Releases the hold taken on the specified share object
657  */
658 void
smb_kshare_release(smb_server_t * sv,smb_kshare_t * shr)659 smb_kshare_release(smb_server_t *sv, smb_kshare_t *shr)
660 {
661 	ASSERT(shr);
662 	ASSERT(shr->shr_magic == SMB_SHARE_MAGIC);
663 
664 	smb_avl_release(&sv->sv_export.e_share_avl, shr);
665 }
666 
667 /*
668  * Add the given share in the specified server.
669  * If the share is a disk share, smb_vfs_hold() is
670  * invoked to ensure that there is a hold on the
671  * corresponding file system before the share is
672  * added to shares AVL.
673  *
674  * If the share is an Autohome share and it is
675  * already in the AVL only a reference count for
676  * that share is incremented.
677  */
678 static int
smb_kshare_export(smb_server_t * sv,smb_kshare_t * shr)679 smb_kshare_export(smb_server_t *sv, smb_kshare_t *shr)
680 {
681 	smb_avl_t	*share_avl;
682 	smb_kshare_t	*auto_shr;
683 	vnode_t		*vp;
684 	int		rc = 0;
685 
686 	share_avl = &sv->sv_export.e_share_avl;
687 
688 	if (!STYPE_ISDSK(shr->shr_type)) {
689 		if ((rc = smb_avl_add(share_avl, shr)) != 0) {
690 			cmn_err(CE_WARN, "export[%s]: failed caching (%d)",
691 			    shr->shr_name, rc);
692 		}
693 
694 		return (rc);
695 	}
696 
697 	if ((auto_shr = smb_avl_lookup(share_avl, shr)) != NULL) {
698 		if ((auto_shr->shr_flags & SMB_SHRF_AUTOHOME) == 0) {
699 			smb_avl_release(share_avl, auto_shr);
700 			return (EEXIST);
701 		}
702 
703 		mutex_enter(&auto_shr->shr_mutex);
704 		auto_shr->shr_autocnt++;
705 		mutex_exit(&auto_shr->shr_mutex);
706 		smb_avl_release(share_avl, auto_shr);
707 		return (0);
708 	}
709 
710 	if ((rc = smb_server_sharevp(sv, shr->shr_path, &vp)) != 0) {
711 		cmn_err(CE_WARN, "export[%s(%s)]: failed obtaining vnode (%d)",
712 		    shr->shr_name, shr->shr_path, rc);
713 		return (rc);
714 	}
715 
716 	if ((rc = smb_vfs_hold(&sv->sv_export, vp->v_vfsp)) == 0) {
717 		if ((rc = smb_avl_add(share_avl, shr)) != 0) {
718 			cmn_err(CE_WARN, "export[%s]: failed caching (%d)",
719 			    shr->shr_name, rc);
720 			smb_vfs_rele(&sv->sv_export, vp->v_vfsp);
721 		}
722 	} else {
723 		cmn_err(CE_WARN, "export[%s(%s)]: failed holding VFS (%d)",
724 		    shr->shr_name, shr->shr_path, rc);
725 	}
726 
727 	VN_RELE(vp);
728 	return (rc);
729 }
730 
731 /*
732  * Removes the share specified by 'shrname' from the AVL
733  * tree of the given server if it's there.
734  *
735  * If the share is an Autohome share, the autohome count
736  * is decremented and the share is only removed if the
737  * count goes to zero.
738  *
739  * If the share is a disk share, the hold on the corresponding
740  * file system is released before removing the share from
741  * the AVL tree.
742  */
743 static int
smb_kshare_unexport(smb_server_t * sv,const char * shrname)744 smb_kshare_unexport(smb_server_t *sv, const char *shrname)
745 {
746 	smb_avl_t	*share_avl;
747 	smb_kshare_t	key;
748 	smb_kshare_t	*shr;
749 	vnode_t		*vp;
750 	int		rc;
751 	boolean_t	auto_unexport;
752 
753 	share_avl = &sv->sv_export.e_share_avl;
754 
755 	key.shr_name = (char *)shrname;
756 	if ((shr = smb_avl_lookup(share_avl, &key)) == NULL)
757 		return (ENOENT);
758 
759 	if ((shr->shr_flags & SMB_SHRF_AUTOHOME) != 0) {
760 		mutex_enter(&shr->shr_mutex);
761 		shr->shr_autocnt--;
762 		auto_unexport = (shr->shr_autocnt == 0);
763 		mutex_exit(&shr->shr_mutex);
764 		if (!auto_unexport) {
765 			smb_avl_release(share_avl, shr);
766 			return (0);
767 		}
768 	}
769 
770 	if (STYPE_ISDSK(shr->shr_type)) {
771 		if ((rc = smb_server_sharevp(sv, shr->shr_path, &vp)) != 0) {
772 			smb_avl_release(share_avl, shr);
773 			cmn_err(CE_WARN, "unexport[%s]: failed obtaining vnode"
774 			    " (%d)", shrname, rc);
775 			return (rc);
776 		}
777 
778 		smb_vfs_rele(&sv->sv_export, vp->v_vfsp);
779 		VN_RELE(vp);
780 	}
781 
782 	smb_avl_remove(share_avl, shr);
783 	smb_avl_release(share_avl, shr);
784 
785 	return (0);
786 }
787 
788 /*
789  * Exports IPC$ or Admin shares
790  */
791 static int
smb_kshare_export_trans(smb_server_t * sv,char * name,char * path,char * cmnt)792 smb_kshare_export_trans(smb_server_t *sv, char *name, char *path, char *cmnt)
793 {
794 	smb_kshare_t *shr;
795 
796 	ASSERT(name);
797 	ASSERT(path);
798 
799 	shr = kmem_cache_alloc(smb_kshare_cache_share, KM_SLEEP);
800 	bzero(shr, sizeof (smb_kshare_t));
801 
802 	shr->shr_magic = SMB_SHARE_MAGIC;
803 	shr->shr_refcnt = 1;
804 	shr->shr_flags = SMB_SHRF_TRANS | smb_kshare_is_admin(shr->shr_name);
805 	if (strcasecmp(name, "IPC$") == 0)
806 		shr->shr_type = STYPE_IPC;
807 	else
808 		shr->shr_type = STYPE_DISKTREE;
809 
810 	shr->shr_type |= smb_kshare_is_special(shr->shr_name);
811 
812 	shr->shr_name = smb_mem_strdup(name);
813 	if (path)
814 		shr->shr_path = smb_mem_strdup(path);
815 	if (cmnt)
816 		shr->shr_cmnt = smb_mem_strdup(cmnt);
817 	shr->shr_oemname = smb_kshare_oemname(name);
818 
819 	return (smb_kshare_export(sv, shr));
820 }
821 
822 /*
823  * Decodes share information in an nvlist format into a smb_kshare_t
824  * structure.
825  *
826  * This is a temporary function and will be replaced by functions
827  * provided by libsharev2 code after it's available.
828  */
829 static smb_kshare_t *
smb_kshare_decode(nvlist_t * share)830 smb_kshare_decode(nvlist_t *share)
831 {
832 	smb_kshare_t tmp;
833 	smb_kshare_t *shr;
834 	nvlist_t *smb;
835 	char *csc_name = NULL;
836 	int rc;
837 
838 	ASSERT(share);
839 
840 	bzero(&tmp, sizeof (smb_kshare_t));
841 
842 	rc = nvlist_lookup_string(share, "name", &tmp.shr_name);
843 	rc |= nvlist_lookup_string(share, "path", &tmp.shr_path);
844 	(void) nvlist_lookup_string(share, "desc", &tmp.shr_cmnt);
845 
846 	ASSERT(tmp.shr_name && tmp.shr_path);
847 
848 	rc |= nvlist_lookup_nvlist(share, "smb", &smb);
849 	if (rc != 0) {
850 		cmn_err(CE_WARN, "kshare: failed looking up SMB properties"
851 		    " (%d)", rc);
852 		return (NULL);
853 	}
854 
855 	rc = nvlist_lookup_uint32(smb, "type", &tmp.shr_type);
856 	if (rc != 0) {
857 		cmn_err(CE_WARN, "kshare[%s]: failed getting the share type"
858 		    " (%d)", tmp.shr_name, rc);
859 		return (NULL);
860 	}
861 
862 	(void) nvlist_lookup_string(smb, SHOPT_AD_CONTAINER,
863 	    &tmp.shr_container);
864 	(void) nvlist_lookup_string(smb, SHOPT_NONE, &tmp.shr_access_none);
865 	(void) nvlist_lookup_string(smb, SHOPT_RO, &tmp.shr_access_ro);
866 	(void) nvlist_lookup_string(smb, SHOPT_RW, &tmp.shr_access_rw);
867 
868 	tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_ABE, SMB_SHRF_ABE);
869 	tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_CATIA,
870 	    SMB_SHRF_CATIA);
871 	tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_GUEST,
872 	    SMB_SHRF_GUEST_OK);
873 	tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_DFSROOT,
874 	    SMB_SHRF_DFSROOT);
875 	tmp.shr_flags |= smb_kshare_decode_bool(smb, "Autohome",
876 	    SMB_SHRF_AUTOHOME);
877 
878 	if ((tmp.shr_flags & SMB_SHRF_AUTOHOME) == SMB_SHRF_AUTOHOME) {
879 		rc = nvlist_lookup_uint32(smb, "uid", &tmp.shr_uid);
880 		rc |= nvlist_lookup_uint32(smb, "gid", &tmp.shr_gid);
881 		if (rc != 0) {
882 			cmn_err(CE_WARN, "kshare: failed looking up uid/gid"
883 			    " (%d)", rc);
884 			return (NULL);
885 		}
886 	}
887 
888 	(void) nvlist_lookup_string(smb, SHOPT_CSC, &csc_name);
889 	smb_kshare_csc_flags(&tmp, csc_name);
890 
891 	shr = kmem_cache_alloc(smb_kshare_cache_share, KM_SLEEP);
892 	bzero(shr, sizeof (smb_kshare_t));
893 
894 	shr->shr_magic = SMB_SHARE_MAGIC;
895 	shr->shr_refcnt = 1;
896 
897 	shr->shr_name = smb_mem_strdup(tmp.shr_name);
898 	shr->shr_path = smb_mem_strdup(tmp.shr_path);
899 	if (tmp.shr_cmnt)
900 		shr->shr_cmnt = smb_mem_strdup(tmp.shr_cmnt);
901 	if (tmp.shr_container)
902 		shr->shr_container = smb_mem_strdup(tmp.shr_container);
903 	if (tmp.shr_access_none)
904 		shr->shr_access_none = smb_mem_strdup(tmp.shr_access_none);
905 	if (tmp.shr_access_ro)
906 		shr->shr_access_ro = smb_mem_strdup(tmp.shr_access_ro);
907 	if (tmp.shr_access_rw)
908 		shr->shr_access_rw = smb_mem_strdup(tmp.shr_access_rw);
909 
910 	shr->shr_oemname = smb_kshare_oemname(shr->shr_name);
911 	shr->shr_flags = tmp.shr_flags | smb_kshare_is_admin(shr->shr_name);
912 	shr->shr_type = tmp.shr_type | smb_kshare_is_special(shr->shr_name);
913 
914 	shr->shr_uid = tmp.shr_uid;
915 	shr->shr_gid = tmp.shr_gid;
916 
917 	if ((shr->shr_flags & SMB_SHRF_AUTOHOME) == SMB_SHRF_AUTOHOME)
918 		shr->shr_autocnt = 1;
919 
920 	return (shr);
921 }
922 
923 #if 0
924 static void
925 smb_kshare_log(smb_kshare_t *shr)
926 {
927 	cmn_err(CE_NOTE, "Share info:");
928 	cmn_err(CE_NOTE, "\tname: %s", (shr->shr_name) ? shr->shr_name : "");
929 	cmn_err(CE_NOTE, "\tpath: %s", (shr->shr_path) ? shr->shr_path : "");
930 	cmn_err(CE_NOTE, "\tcmnt: (%s)",
931 	    (shr->shr_cmnt) ? shr->shr_cmnt : "NULL");
932 	cmn_err(CE_NOTE, "\toemname: (%s)",
933 	    (shr->shr_oemname) ? shr->shr_oemname : "NULL");
934 	cmn_err(CE_NOTE, "\tflags: %X", shr->shr_flags);
935 	cmn_err(CE_NOTE, "\ttype: %d", shr->shr_type);
936 }
937 #endif
938 
939 /*
940  * Compare function used by shares AVL
941  */
942 static int
smb_kshare_cmp(const void * p1,const void * p2)943 smb_kshare_cmp(const void *p1, const void *p2)
944 {
945 	smb_kshare_t *shr1 = (smb_kshare_t *)p1;
946 	smb_kshare_t *shr2 = (smb_kshare_t *)p2;
947 	int rc;
948 
949 	ASSERT(shr1);
950 	ASSERT(shr1->shr_name);
951 
952 	ASSERT(shr2);
953 	ASSERT(shr2->shr_name);
954 
955 	rc = smb_strcasecmp(shr1->shr_name, shr2->shr_name, 0);
956 
957 	if (rc < 0)
958 		return (-1);
959 
960 	if (rc > 0)
961 		return (1);
962 
963 	return (0);
964 }
965 
966 /*
967  * This function is called by smb_avl routines whenever
968  * there is a need to take a hold on a share structure
969  * inside AVL
970  */
971 static void
smb_kshare_hold(const void * p)972 smb_kshare_hold(const void *p)
973 {
974 	smb_kshare_t *shr = (smb_kshare_t *)p;
975 
976 	ASSERT(shr);
977 	ASSERT(shr->shr_magic == SMB_SHARE_MAGIC);
978 
979 	mutex_enter(&shr->shr_mutex);
980 	shr->shr_refcnt++;
981 	mutex_exit(&shr->shr_mutex);
982 }
983 
984 /*
985  * This function must be called by smb_avl routines whenever
986  * smb_kshare_hold is called and the hold needs to be released.
987  */
988 static boolean_t
smb_kshare_rele(const void * p)989 smb_kshare_rele(const void *p)
990 {
991 	smb_kshare_t *shr = (smb_kshare_t *)p;
992 	boolean_t destroy;
993 
994 	ASSERT(shr);
995 	ASSERT(shr->shr_magic == SMB_SHARE_MAGIC);
996 
997 	mutex_enter(&shr->shr_mutex);
998 	ASSERT(shr->shr_refcnt > 0);
999 	shr->shr_refcnt--;
1000 	destroy = (shr->shr_refcnt == 0);
1001 	mutex_exit(&shr->shr_mutex);
1002 
1003 	return (destroy);
1004 }
1005 
1006 /*
1007  * Frees all the memory allocated for the given
1008  * share structure. It also removes the structure
1009  * from the share cache.
1010  */
1011 static void
smb_kshare_destroy(void * p)1012 smb_kshare_destroy(void *p)
1013 {
1014 	smb_kshare_t *shr = (smb_kshare_t *)p;
1015 
1016 	ASSERT(shr);
1017 	ASSERT(shr->shr_magic == SMB_SHARE_MAGIC);
1018 
1019 	smb_mem_free(shr->shr_name);
1020 	smb_mem_free(shr->shr_path);
1021 	smb_mem_free(shr->shr_cmnt);
1022 	smb_mem_free(shr->shr_container);
1023 	smb_mem_free(shr->shr_oemname);
1024 	smb_mem_free(shr->shr_access_none);
1025 	smb_mem_free(shr->shr_access_ro);
1026 	smb_mem_free(shr->shr_access_rw);
1027 
1028 	kmem_cache_free(smb_kshare_cache_share, shr);
1029 }
1030 
1031 
1032 /*
1033  * Generate an OEM name for the given share name.  If the name is
1034  * shorter than 13 bytes the oemname will be returned; otherwise NULL
1035  * is returned.
1036  */
1037 static char *
smb_kshare_oemname(const char * shrname)1038 smb_kshare_oemname(const char *shrname)
1039 {
1040 	smb_wchar_t *unibuf;
1041 	char *oem_name;
1042 	int length;
1043 
1044 	length = strlen(shrname) + 1;
1045 
1046 	oem_name = smb_mem_alloc(length);
1047 	unibuf = smb_mem_alloc(length * sizeof (smb_wchar_t));
1048 
1049 	(void) smb_mbstowcs(unibuf, shrname, length);
1050 
1051 	if (ucstooem(oem_name, unibuf, length, OEM_CPG_850) == 0)
1052 		(void) strcpy(oem_name, shrname);
1053 
1054 	smb_mem_free(unibuf);
1055 
1056 	if (strlen(oem_name) + 1 > SMB_SHARE_OEMNAME_MAX) {
1057 		smb_mem_free(oem_name);
1058 		return (NULL);
1059 	}
1060 
1061 	return (oem_name);
1062 }
1063 
1064 /*
1065  * Special share reserved for interprocess communication (IPC$) or
1066  * remote administration of the server (ADMIN$). Can also refer to
1067  * administrative shares such as C$, D$, E$, and so forth.
1068  */
1069 static int
smb_kshare_is_special(const char * sharename)1070 smb_kshare_is_special(const char *sharename)
1071 {
1072 	int len;
1073 
1074 	if (sharename == NULL)
1075 		return (0);
1076 
1077 	if ((len = strlen(sharename)) == 0)
1078 		return (0);
1079 
1080 	if (sharename[len - 1] == '$')
1081 		return (STYPE_SPECIAL);
1082 
1083 	return (0);
1084 }
1085 
1086 /*
1087  * Check whether or not this is a default admin share: C$, D$ etc.
1088  */
1089 static boolean_t
smb_kshare_is_admin(const char * sharename)1090 smb_kshare_is_admin(const char *sharename)
1091 {
1092 	if (sharename == NULL)
1093 		return (B_FALSE);
1094 
1095 	if (strlen(sharename) == 2 &&
1096 	    smb_isalpha(sharename[0]) && sharename[1] == '$') {
1097 		return (B_TRUE);
1098 	}
1099 
1100 	return (B_FALSE);
1101 }
1102 
1103 /*
1104  * Decodes the given boolean share option.
1105  * If the option is present in the nvlist and it's value is true
1106  * returns the corresponding flag value, otherwise returns 0.
1107  */
1108 static uint32_t
smb_kshare_decode_bool(nvlist_t * nvl,const char * propname,uint32_t flag)1109 smb_kshare_decode_bool(nvlist_t *nvl, const char *propname, uint32_t flag)
1110 {
1111 	char *boolp;
1112 
1113 	if (nvlist_lookup_string(nvl, propname, &boolp) == 0)
1114 		if (strcasecmp(boolp, "true") == 0)
1115 			return (flag);
1116 
1117 	return (0);
1118 }
1119 
1120 /*
1121  * Map a client-side caching (CSC) option to the appropriate share
1122  * flag.  Only one option is allowed; an error will be logged if
1123  * multiple options have been specified.  We don't need to do anything
1124  * about multiple values here because the SRVSVC will not recognize
1125  * a value containing multiple flags and will return the default value.
1126  *
1127  * If the option value is not recognized, it will be ignored: invalid
1128  * values will typically be caught and rejected by sharemgr.
1129  */
1130 static void
smb_kshare_csc_flags(smb_kshare_t * shr,const char * value)1131 smb_kshare_csc_flags(smb_kshare_t *shr, const char *value)
1132 {
1133 	int i;
1134 	static struct {
1135 		char *value;
1136 		uint32_t flag;
1137 	} cscopt[] = {
1138 		{ "disabled",	SMB_SHRF_CSC_DISABLED },
1139 		{ "manual",	SMB_SHRF_CSC_MANUAL },
1140 		{ "auto",	SMB_SHRF_CSC_AUTO },
1141 		{ "vdo",	SMB_SHRF_CSC_VDO }
1142 	};
1143 
1144 	if (value == NULL)
1145 		return;
1146 
1147 	for (i = 0; i < (sizeof (cscopt) / sizeof (cscopt[0])); ++i) {
1148 		if (strcasecmp(value, cscopt[i].value) == 0) {
1149 			shr->shr_flags |= cscopt[i].flag;
1150 			break;
1151 		}
1152 	}
1153 
1154 	switch (shr->shr_flags & SMB_SHRF_CSC_MASK) {
1155 	case 0:
1156 	case SMB_SHRF_CSC_DISABLED:
1157 	case SMB_SHRF_CSC_MANUAL:
1158 	case SMB_SHRF_CSC_AUTO:
1159 	case SMB_SHRF_CSC_VDO:
1160 		break;
1161 
1162 	default:
1163 		cmn_err(CE_NOTE, "csc option conflict: 0x%08x",
1164 		    shr->shr_flags & SMB_SHRF_CSC_MASK);
1165 		break;
1166 	}
1167 }
1168 
1169 /*
1170  * This function processes the unexport event list and disconnects shares
1171  * asynchronously.  The function executes as a zone-specific thread.
1172  *
1173  * The server arg passed in is safe to use without a reference count, because
1174  * the server cannot be deleted until smb_thread_stop()/destroy() return,
1175  * which is also when the thread exits.
1176  */
1177 /*ARGSUSED*/
1178 static void
smb_kshare_unexport_thread(smb_thread_t * thread,void * arg)1179 smb_kshare_unexport_thread(smb_thread_t *thread, void *arg)
1180 {
1181 	smb_server_t	*sv = arg;
1182 	smb_unshare_t	*ux;
1183 
1184 	while (smb_thread_continue(thread)) {
1185 		while ((ux = list_head(&sv->sv_export.e_unexport_list.sl_list))
1186 		    != NULL) {
1187 			smb_slist_remove(&sv->sv_export.e_unexport_list, ux);
1188 			(void) smb_server_unshare(ux->us_sharename);
1189 			kmem_cache_free(smb_kshare_cache_unexport, ux);
1190 		}
1191 	}
1192 }
1193 
1194 static boolean_t
smb_export_isready(smb_server_t * sv)1195 smb_export_isready(smb_server_t *sv)
1196 {
1197 	boolean_t ready;
1198 
1199 	mutex_enter(&sv->sv_export.e_mutex);
1200 	ready = sv->sv_export.e_ready;
1201 	mutex_exit(&sv->sv_export.e_mutex);
1202 
1203 	return (ready);
1204 }
1205 
1206 #ifdef	_KERNEL
1207 /*
1208  * Return 0 upon success. Otherwise > 0
1209  */
1210 static int
smb_kshare_chk_dsrv_status(int opcode,smb_dr_ctx_t * dec_ctx)1211 smb_kshare_chk_dsrv_status(int opcode, smb_dr_ctx_t *dec_ctx)
1212 {
1213 	int status = smb_dr_get_int32(dec_ctx);
1214 	int err;
1215 
1216 	switch (status) {
1217 	case SMB_SHARE_DSUCCESS:
1218 		return (0);
1219 
1220 	case SMB_SHARE_DERROR:
1221 		err = smb_dr_get_uint32(dec_ctx);
1222 		cmn_err(CE_WARN, "%d: Encountered door server error %d",
1223 		    opcode, err);
1224 		(void) smb_dr_decode_finish(dec_ctx);
1225 		return (err);
1226 	}
1227 
1228 	ASSERT(0);
1229 	return (EINVAL);
1230 }
1231 #endif	/* _KERNEL */
1232