xref: /titanic_50/usr/src/uts/common/fs/smbsrv/smb_fsops.c (revision 1ba18ff1efb9bb19540297cbee0a824685da1622)
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  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/sid.h>
29 #include <smbsrv/smb_incl.h>
30 #include <smbsrv/smb_fsops.h>
31 #include <acl/acl_common.h>
32 
33 u_longlong_t smb_caller_id;
34 
35 static int smb_fsop_amask_to_omode(uint32_t granted_access);
36 
37 extern uint32_t smb_sd_tofs(smb_sdbuf_t *sr_sd, smb_fssd_t *fs_sd);
38 
39 extern uint32_t smb_sd_write(smb_request_t *sr, smb_sdbuf_t *sr_sd,
40     uint32_t secinfo);
41 
42 extern int smb_vop_acl_to_vsa(acl_t *acl_info, vsecattr_t *vsecattr,
43     int *aclbsize);
44 
45 static int smb_fsop_sdinherit(smb_request_t *sr, smb_node_t *dnode,
46     smb_fssd_t *fs_sd);
47 
48 static void smb_fsop_aclsplit(acl_t *zacl, acl_t **dacl, acl_t **sacl,
49     int which_acl);
50 static acl_t *smb_fsop_aclmerge(acl_t *dacl, acl_t *sacl);
51 
52 /*
53  * The smb_fsop_* functions have knowledge of CIFS semantics.
54  *
55  * The smb_vop_* functions have minimal knowledge of CIFS semantics and
56  * serve as an interface to the VFS layer.
57  *
58  * Hence, smb_request_t and smb_node_t structures should not be passed
59  * from the smb_fsop_* layer to the smb_vop_* layer.
60  *
61  * In general, CIFS service code should only ever call smb_fsop_*
62  * functions directly, and never smb_vop_* functions directly.
63  *
64  * smb_fsop_* functions should call smb_vop_* functions where possible, instead
65  * of their smb_fsop_* counterparts.  However, there are times when
66  * this cannot be avoided.
67  */
68 
69 /*
70  * Note: Stream names cannot be mangled.
71  */
72 
73 int
74 smb_fsop_start()
75 {
76 	int error;
77 
78 	smb_caller_id = fs_new_caller_id();
79 	error = smb_node_root_init();
80 
81 	if (error == 0)
82 		error = smb_fem_init();
83 
84 	return (error);
85 }
86 
87 void
88 smb_fsop_stop()
89 {
90 	smb_fem_shutdown();
91 	smb_vfs_rele_all();
92 	smb_node_root_fini();
93 }
94 
95 int
96 smb_fsop_open(smb_ofile_t *of)
97 {
98 	caller_context_t ct;
99 	int mode;
100 
101 	mode = smb_fsop_amask_to_omode(of->f_granted_access);
102 
103 	smb_get_caller_context(NULL, &ct);
104 
105 	/*
106 	 * Assuming that same vnode is returned as we had before
107 	 * (i.e. no special vnodes)
108 	 */
109 
110 	return (smb_vop_open(&of->f_node->vp, mode, of->f_cr, &ct));
111 }
112 
113 int
114 smb_fsop_close(smb_ofile_t *of)
115 {
116 	caller_context_t ct;
117 	int mode;
118 
119 	mode = smb_fsop_amask_to_omode(of->f_granted_access);
120 
121 	smb_get_caller_context(NULL, &ct);
122 
123 	return (smb_vop_close(of->f_node->vp, mode, of->f_cr, &ct));
124 }
125 
126 static int
127 smb_fsop_amask_to_omode(uint32_t granted_access)
128 {
129 	int mode = 0;
130 
131 	if (granted_access & (ACE_READ_DATA | ACE_EXECUTE))
132 		mode |= FREAD;
133 
134 	if (granted_access & (ACE_WRITE_DATA | ACE_APPEND_DATA))
135 		mode |= FWRITE;
136 
137 	if (granted_access & ACE_APPEND_DATA)
138 		mode |= FAPPEND;
139 
140 	return (mode);
141 }
142 
143 static int
144 smb_fsop_create_with_sd(
145 	struct smb_request *sr,
146 	cred_t *cr,
147 	smb_node_t *snode,
148 	char *name,
149 	smb_attr_t *attr,
150 	smb_node_t **ret_snode,
151 	smb_attr_t *ret_attr,
152 	smb_fssd_t *fs_sd)
153 {
154 	caller_context_t ct;
155 	vsecattr_t *vsap;
156 	vsecattr_t vsecattr;
157 	acl_t *acl, *dacl, *sacl;
158 	smb_attr_t set_attr;
159 	vnode_t *vp;
160 	int aclbsize = 0;	/* size of acl list in bytes */
161 	int flags = 0;
162 	int is_dir;
163 	int rc;
164 
165 	ASSERT(fs_sd);
166 
167 	if (SMB_TREE_CASE_INSENSITIVE(sr))
168 		flags = SMB_IGNORE_CASE;
169 
170 	ASSERT(cr);
171 	smb_get_caller_context(sr, &ct);
172 
173 	is_dir = ((fs_sd->sd_flags & SMB_FSSD_FLAGS_DIR) != 0);
174 
175 	if (sr->tid_tree->t_flags & SMB_TREE_FLAG_ACLONCREATE) {
176 		if (fs_sd->sd_secinfo & SMB_ACL_SECINFO) {
177 			dacl = fs_sd->sd_zdacl;
178 			sacl = fs_sd->sd_zsacl;
179 			ASSERT(dacl || sacl);
180 			if (dacl && sacl) {
181 				acl = smb_fsop_aclmerge(dacl, sacl);
182 			} else if (dacl) {
183 				acl = dacl;
184 			} else {
185 				acl = sacl;
186 			}
187 
188 			rc = smb_vop_acl_to_vsa(acl, &vsecattr, &aclbsize);
189 
190 			if (dacl && sacl)
191 				acl_free(acl);
192 
193 			if (rc)
194 				return (rc);
195 
196 			vsap = &vsecattr;
197 		}
198 		else
199 			vsap = NULL;
200 
201 		if (is_dir) {
202 			rc = smb_vop_mkdir(snode->vp, name, attr, &vp, flags,
203 			    cr, &ct, vsap);
204 		} else {
205 			rc = smb_vop_create(snode->vp, name, attr, &vp, flags,
206 			    cr, &ct, vsap);
207 		}
208 
209 		if (vsap != NULL)
210 			kmem_free(vsap->vsa_aclentp, aclbsize);
211 
212 		if (rc != 0)
213 			return (rc);
214 
215 		set_attr.sa_mask = 0;
216 
217 		/*
218 		 * Ideally we should be able to specify the owner and owning
219 		 * group at create time along with the ACL. Since we cannot
220 		 * do that right now, kcred is passed to smb_vop_setattr so it
221 		 * doesn't fail due to lack of permission.
222 		 */
223 		if (fs_sd->sd_secinfo & SMB_OWNER_SECINFO) {
224 			set_attr.sa_vattr.va_uid = fs_sd->sd_uid;
225 			set_attr.sa_mask |= SMB_AT_UID;
226 		}
227 
228 		if (fs_sd->sd_secinfo & SMB_GROUP_SECINFO) {
229 			set_attr.sa_vattr.va_gid = fs_sd->sd_gid;
230 			set_attr.sa_mask |= SMB_AT_GID;
231 		}
232 
233 		if (set_attr.sa_mask) {
234 			rc = smb_vop_setattr(snode->vp, NULL, &set_attr,
235 			    0, kcred, &ct);
236 		}
237 
238 	} else {
239 		/*
240 		 * For filesystems that don't support ACL-on-create, try
241 		 * to set the specified SD after create, which could actually
242 		 * fail because of conflicts between inherited security
243 		 * attributes upon creation and the specified SD.
244 		 *
245 		 * Passing kcred to smb_fsop_sdwrite() to overcome this issue.
246 		 */
247 
248 		if (is_dir) {
249 			rc = smb_vop_mkdir(snode->vp, name, attr, &vp, flags,
250 			    cr, &ct, NULL);
251 		} else {
252 			rc = smb_vop_create(snode->vp, name, attr, &vp, flags,
253 			    cr, &ct, NULL);
254 		}
255 
256 		if (rc == 0)
257 			rc = smb_fsop_sdwrite(sr, kcred, snode, fs_sd, 1);
258 	}
259 
260 	if (rc == 0) {
261 		*ret_snode = smb_node_lookup(sr, &sr->arg.open, cr, vp, name,
262 		    snode, NULL, ret_attr);
263 
264 		if (*ret_snode == NULL) {
265 			VN_RELE(vp);
266 			rc = ENOMEM;
267 		}
268 	}
269 
270 	return (rc);
271 }
272 
273 
274 /*
275  * smb_fsop_create
276  *
277  * All SMB functions should use this wrapper to ensure that
278  * all the smb_vop_creates are performed with the appropriate credentials.
279  * Please document any direct calls to explain the reason
280  * for avoiding this wrapper.
281  *
282  * It is assumed that a reference exists on snode coming into this routine.
283  *
284  * *ret_snode is returned with a reference upon success.  No reference is
285  * taken if an error is returned.
286  */
287 
288 int
289 smb_fsop_create(
290     struct smb_request *sr,
291     cred_t *cr,
292     smb_node_t *dir_snode,
293     char *name,
294     smb_attr_t *attr,
295     smb_node_t **ret_snode,
296     smb_attr_t *ret_attr)
297 {
298 	struct open_param *op = &sr->arg.open;
299 	smb_node_t *fnode;
300 	smb_attr_t file_attr;
301 	caller_context_t ct;
302 	vnode_t *xattrdirvp;
303 	vnode_t *vp;
304 	char *longname = NULL;
305 	char *namep;
306 	char *fname;
307 	char *sname;
308 	int is_stream;
309 	int flags = 0;
310 	int rc = 0;
311 	smb_fssd_t fs_sd;
312 	uint32_t secinfo;
313 	uint32_t status;
314 
315 	ASSERT(cr);
316 	ASSERT(dir_snode);
317 	ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC);
318 	ASSERT(dir_snode->n_state != SMB_NODE_STATE_DESTROYING);
319 
320 	ASSERT(ret_snode);
321 	*ret_snode = 0;
322 
323 	ASSERT(name);
324 	if (*name == 0)
325 		return (EINVAL);
326 
327 	if (SMB_TREE_ROOT_FS(sr, dir_snode) == 0)
328 		return (EACCES);
329 
330 	ASSERT(sr);
331 	ASSERT(sr->tid_tree);
332 	if (SMB_TREE_IS_READ_ONLY(sr))
333 		return (EROFS);
334 
335 	if (SMB_TREE_CASE_INSENSITIVE(sr))
336 		flags = SMB_IGNORE_CASE;
337 
338 	fname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
339 	sname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
340 
341 	is_stream = smb_stream_parse_name(name, fname, sname);
342 
343 	if (is_stream)
344 		namep = fname;
345 	else
346 		namep = name;
347 
348 	if (smb_maybe_mangled_name(namep)) {
349 		longname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
350 
351 		rc = smb_unmangle_name(sr, cr, dir_snode, namep, longname,
352 		    MAXNAMELEN, NULL, NULL, 1);
353 
354 		if ((is_stream == 0) && (rc == 0))
355 			rc = EEXIST;
356 
357 		if ((is_stream && rc) ||
358 		    ((is_stream == 0) && (rc != ENOENT))) {
359 			kmem_free(longname, MAXNAMELEN);
360 			kmem_free(fname, MAXNAMELEN);
361 			kmem_free(sname, MAXNAMELEN);
362 			return (rc);
363 		}
364 
365 		if (is_stream)
366 			namep = longname;
367 		else
368 			kmem_free(longname, MAXNAMELEN);
369 	}
370 
371 	if (is_stream) {
372 		/*
373 		 * Look up the unnamed stream.
374 		 *
375 		 * Mangle processing in smb_fsop_lookup() for the unnamed
376 		 * stream won't be needed (as it was done above), but
377 		 * it may be needed on any link target (which
378 		 * smb_fsop_lookup() will provide).
379 		 */
380 		rc = smb_fsop_lookup(sr, cr, flags | SMB_FOLLOW_LINKS,
381 		    sr->tid_tree->t_snode, dir_snode, namep, &fnode, &file_attr,
382 		    0, 0);
383 
384 		if (longname) {
385 			kmem_free(longname, MAXNAMELEN);
386 			namep = NULL;
387 		}
388 
389 		if (rc != 0) {
390 			kmem_free(fname, MAXNAMELEN);
391 			kmem_free(sname, MAXNAMELEN);
392 			return (rc);
393 		}
394 
395 		smb_get_caller_context(sr, &ct);
396 
397 		rc = smb_vop_stream_create(fnode->vp, sname, attr, &vp,
398 		    &xattrdirvp, flags, cr, &ct);
399 
400 		if (rc != 0) {
401 			smb_node_release(fnode);
402 			kmem_free(fname, MAXNAMELEN);
403 			kmem_free(sname, MAXNAMELEN);
404 			return (rc);
405 		}
406 
407 		*ret_snode = smb_stream_node_lookup(sr, cr, fnode, xattrdirvp,
408 		    vp, sname, ret_attr);
409 
410 		smb_node_release(fnode);
411 
412 		if (*ret_snode == NULL) {
413 			VN_RELE(xattrdirvp);
414 			VN_RELE(vp);
415 			kmem_free(fname, MAXNAMELEN);
416 			kmem_free(sname, MAXNAMELEN);
417 			return (ENOMEM);
418 		}
419 	} else {
420 		if (op->sd_buf) {
421 			/*
422 			 * SD sent by client in Windows format. Needs to be
423 			 * converted to FS format. No inheritance.
424 			 */
425 			secinfo = smb_sd_get_secinfo((smb_sdbuf_t *)op->sd_buf);
426 			smb_fsop_sdinit(&fs_sd, secinfo, 0);
427 
428 			status = smb_sd_tofs(op->sd_buf, &fs_sd);
429 			if (status == NT_STATUS_SUCCESS) {
430 				rc = smb_fsop_create_with_sd(sr, cr, dir_snode,
431 				    name, attr, ret_snode, ret_attr, &fs_sd);
432 			}
433 			else
434 				rc = EINVAL;
435 			smb_fsop_sdterm(&fs_sd);
436 		} else if (sr->tid_tree->t_acltype == ACE_T) {
437 			/*
438 			 * No incoming SD and filesystem is ZFS
439 			 * Server applies Windows inheritance rules,
440 			 * see smb_fsop_sdinherit() comments as to why.
441 			 */
442 			smb_fsop_sdinit(&fs_sd, SMB_ACL_SECINFO, 0);
443 			rc = smb_fsop_sdinherit(sr, dir_snode, &fs_sd);
444 			if (rc == 0) {
445 				rc = smb_fsop_create_with_sd(sr, cr, dir_snode,
446 				    name, attr, ret_snode, ret_attr, &fs_sd);
447 			}
448 
449 			smb_fsop_sdterm(&fs_sd);
450 		} else {
451 			/*
452 			 * No incoming SD and filesystem is not ZFS
453 			 * let the filesystem handles the inheritance.
454 			 */
455 			smb_get_caller_context(sr, &ct);
456 			rc = smb_vop_create(dir_snode->vp, name, attr, &vp,
457 			    flags, cr, &ct, NULL);
458 
459 			if (rc == 0) {
460 				*ret_snode = smb_node_lookup(sr, op, cr, vp,
461 				    name, dir_snode, NULL, ret_attr);
462 
463 				if (*ret_snode == NULL) {
464 					VN_RELE(vp);
465 					rc = ENOMEM;
466 				}
467 			}
468 
469 		}
470 	}
471 
472 	kmem_free(fname, MAXNAMELEN);
473 	kmem_free(sname, MAXNAMELEN);
474 	return (rc);
475 }
476 
477 /*
478  * smb_fsop_mkdir
479  *
480  * All SMB functions should use this wrapper to ensure that
481  * the the calls are performed with the appropriate credentials.
482  * Please document any direct call to explain the reason
483  * for avoiding this wrapper.
484  *
485  * It is assumed that a reference exists on snode coming into this routine.
486  *
487  * *ret_snode is returned with a reference upon success.  No reference is
488  * taken if an error is returned.
489  */
490 int
491 smb_fsop_mkdir(
492     struct smb_request *sr,
493     cred_t *cr,
494     smb_node_t *dir_snode,
495     char *name,
496     smb_attr_t *attr,
497     smb_node_t **ret_snode,
498     smb_attr_t *ret_attr)
499 {
500 	struct open_param *op = &sr->arg.open;
501 	caller_context_t ct;
502 	char *longname;
503 	vnode_t *vp;
504 	int flags = 0;
505 	smb_fssd_t fs_sd;
506 	uint32_t secinfo;
507 	uint32_t status;
508 	int rc;
509 
510 	ASSERT(cr);
511 	ASSERT(dir_snode);
512 	ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC);
513 	ASSERT(dir_snode->n_state != SMB_NODE_STATE_DESTROYING);
514 
515 	ASSERT(ret_snode);
516 	*ret_snode = 0;
517 
518 	ASSERT(name);
519 	if (*name == 0)
520 		return (EINVAL);
521 
522 	if (SMB_TREE_ROOT_FS(sr, dir_snode) == 0)
523 		return (EACCES);
524 
525 	ASSERT(sr);
526 	ASSERT(sr->tid_tree);
527 	if (SMB_TREE_IS_READ_ONLY(sr))
528 		return (EROFS);
529 
530 	if (smb_maybe_mangled_name(name)) {
531 		longname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
532 		rc = smb_unmangle_name(sr, cr, dir_snode, name, longname,
533 		    MAXNAMELEN, NULL, NULL, 1);
534 
535 		kmem_free(longname, MAXNAMELEN);
536 
537 		/*
538 		 * If the name passed in by the client has an unmangled
539 		 * equivalent that is found in the specified directory,
540 		 * then the mkdir cannot succeed.  Return EEXIST.
541 		 *
542 		 * Only if ENOENT is returned will a mkdir be attempted.
543 		 */
544 
545 		if (rc == 0)
546 			rc = EEXIST;
547 
548 		if (rc != ENOENT)
549 			return (rc);
550 	}
551 
552 	if (SMB_TREE_CASE_INSENSITIVE(sr))
553 		flags = SMB_IGNORE_CASE;
554 
555 	smb_get_caller_context(sr, &ct);
556 
557 	if (op->sd_buf) {
558 		/*
559 		 * SD sent by client in Windows format. Needs to be
560 		 * converted to FS format. No inheritance.
561 		 */
562 		secinfo = smb_sd_get_secinfo((smb_sdbuf_t *)op->sd_buf);
563 		smb_fsop_sdinit(&fs_sd, secinfo, SMB_FSSD_FLAGS_DIR);
564 
565 		status = smb_sd_tofs(op->sd_buf, &fs_sd);
566 		if (status == NT_STATUS_SUCCESS) {
567 			rc = smb_fsop_create_with_sd(sr, cr, dir_snode,
568 			    name, attr, ret_snode, ret_attr, &fs_sd);
569 		}
570 		else
571 			rc = EINVAL;
572 		smb_fsop_sdterm(&fs_sd);
573 	} else if (sr->tid_tree->t_acltype == ACE_T) {
574 		/*
575 		 * No incoming SD and filesystem is ZFS
576 		 * Server applies Windows inheritance rules,
577 		 * see smb_fsop_sdinherit() comments as to why.
578 		 */
579 		smb_fsop_sdinit(&fs_sd, SMB_ACL_SECINFO, SMB_FSSD_FLAGS_DIR);
580 		rc = smb_fsop_sdinherit(sr, dir_snode, &fs_sd);
581 		if (rc == 0) {
582 			rc = smb_fsop_create_with_sd(sr, cr, dir_snode,
583 			    name, attr, ret_snode, ret_attr, &fs_sd);
584 		}
585 
586 		smb_fsop_sdterm(&fs_sd);
587 
588 	} else {
589 		rc = smb_vop_mkdir(dir_snode->vp, name, attr, &vp, flags, cr,
590 		    &ct, NULL);
591 
592 		if (rc == 0) {
593 			*ret_snode = smb_node_lookup(sr, op, cr, vp, name,
594 			    dir_snode, NULL, ret_attr);
595 
596 			if (*ret_snode == NULL) {
597 				VN_RELE(vp);
598 				rc = ENOMEM;
599 			}
600 		}
601 	}
602 
603 	return (rc);
604 }
605 
606 /*
607  * smb_fsop_remove
608  *
609  * All SMB functions should use this wrapper to ensure that
610  * the the calls are performed with the appropriate credentials.
611  * Please document any direct call to explain the reason
612  * for avoiding this wrapper.
613  *
614  * It is assumed that a reference exists on snode coming into this routine.
615  *
616  * od: This means that the name passed in is an on-disk name.
617  * A null smb_request might be passed to this function.
618  */
619 
620 int
621 smb_fsop_remove(
622     struct smb_request *sr,
623     cred_t *cr,
624     smb_node_t *dir_snode,
625     char *name,
626     int od)
627 {
628 	smb_node_t *fnode;
629 	smb_attr_t file_attr;
630 	caller_context_t ct;
631 	char *longname;
632 	char *fname;
633 	char *sname;
634 	int flags = 0;
635 	int rc;
636 
637 	ASSERT(cr);
638 	/*
639 	 * The state of the node could be SMB_NODE_STATE_DESTROYING if this
640 	 * function is called during the deletion of the node (because of
641 	 * DELETE_ON_CLOSE).
642 	 */
643 	ASSERT(dir_snode);
644 	ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC);
645 
646 	if (SMB_TREE_ROOT_FS(sr, dir_snode) == 0)
647 		return (EACCES);
648 
649 	if (SMB_TREE_IS_READ_ONLY(sr))
650 		return (EROFS);
651 
652 	smb_get_caller_context(sr, &ct);
653 
654 	fname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
655 	sname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
656 
657 	if (smb_stream_parse_name(name, fname, sname)) {
658 
659 		ASSERT(od == 0);
660 
661 		if (SMB_TREE_CASE_INSENSITIVE(sr))
662 			flags = SMB_IGNORE_CASE;
663 
664 		/*
665 		 * Look up the unnamed stream (i.e. fname).
666 		 * Unmangle processing will be done on fname
667 		 * as well as any link target.
668 		 */
669 
670 		rc = smb_fsop_lookup(sr, cr, flags | SMB_FOLLOW_LINKS,
671 		    sr->tid_tree->t_snode, dir_snode, fname, &fnode, &file_attr,
672 		    0, 0);
673 
674 		if (rc != 0) {
675 			kmem_free(fname, MAXNAMELEN);
676 			kmem_free(sname, MAXNAMELEN);
677 			return (rc);
678 		}
679 
680 		/*
681 		 * XXX
682 		 * Need to find out what permission is required by NTFS
683 		 * to remove a stream.
684 		 */
685 		rc = smb_vop_stream_remove(fnode->vp, sname, flags, cr, &ct);
686 
687 		smb_node_release(fnode);
688 	} else {
689 		/*
690 		 * If the passed-in name is an on-disk name,
691 		 * then we need to do a case-sensitive remove.
692 		 * This is important if the on-disk name
693 		 * corresponds to a mangled name passed in by
694 		 * the client.  We want to make sure to remove
695 		 * the exact file specified by the client,
696 		 * instead of letting the underlying file system
697 		 * do a remove on the "first match."
698 		 */
699 
700 		if ((od == 0) && SMB_TREE_CASE_INSENSITIVE(sr))
701 			flags = SMB_IGNORE_CASE;
702 
703 		rc = smb_vop_remove(dir_snode->vp, name, flags, cr, &ct);
704 
705 		if (rc == ENOENT) {
706 			if (smb_maybe_mangled_name(name) == 0) {
707 				kmem_free(fname, MAXNAMELEN);
708 				kmem_free(sname, MAXNAMELEN);
709 				return (rc);
710 			}
711 			longname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
712 
713 			rc = smb_unmangle_name(sr, cr, dir_snode, name,
714 			    longname, MAXNAMELEN, NULL, NULL, 1);
715 
716 			if (rc == 0) {
717 				/*
718 				 * We passed "1" as the "od" parameter
719 				 * to smb_unmangle_name(), such that longname
720 				 * is the real (case-sensitive) on-disk name.
721 				 * We make sure we do a remove on this exact
722 				 * name, as the name was mangled and denotes
723 				 * a unique file.
724 				 */
725 				flags &= ~SMB_IGNORE_CASE;
726 				rc = smb_vop_remove(dir_snode->vp, longname,
727 				    flags, cr, &ct);
728 			}
729 
730 			kmem_free(longname, MAXNAMELEN);
731 		}
732 	}
733 
734 	kmem_free(fname, MAXNAMELEN);
735 	kmem_free(sname, MAXNAMELEN);
736 	return (rc);
737 }
738 
739 /*
740  * smb_fsop_remove_streams
741  *
742  * This function removes a file's streams without removing the
743  * file itself.
744  *
745  * It is assumed that snode is not a link.
746  */
747 int
748 smb_fsop_remove_streams(struct smb_request *sr, cred_t *cr,
749     smb_node_t *fnode)
750 {
751 	struct fs_stream_info stream_info;
752 	caller_context_t ct;
753 	uint32_t cookie = 0;
754 	int flags = 0;
755 	int rc;
756 
757 	ASSERT(cr);
758 	ASSERT(fnode);
759 	ASSERT(fnode->n_magic == SMB_NODE_MAGIC);
760 	ASSERT(fnode->n_state != SMB_NODE_STATE_DESTROYING);
761 
762 	if (SMB_TREE_ROOT_FS(sr, fnode) == 0)
763 		return (EACCES);
764 
765 	if (SMB_TREE_IS_READ_ONLY(sr))
766 		return (EROFS);
767 
768 	if (SMB_TREE_CASE_INSENSITIVE(sr))
769 		flags = SMB_IGNORE_CASE;
770 
771 	smb_get_caller_context(sr, &ct);
772 
773 	for (;;) {
774 		rc = smb_vop_stream_readdir(fnode->vp, &cookie, &stream_info,
775 		    NULL, NULL, flags, cr, &ct);
776 
777 		if ((rc != 0) || (cookie == SMB_EOF))
778 			break;
779 
780 		(void) smb_vop_stream_remove(fnode->vp, stream_info.name, flags,
781 		    cr, &ct);
782 	}
783 	return (rc);
784 }
785 
786 /*
787  * smb_fsop_rmdir
788  *
789  * All SMB functions should use this wrapper to ensure that
790  * the the calls are performed with the appropriate credentials.
791  * Please document any direct call to explain the reason
792  * for avoiding this wrapper.
793  *
794  * It is assumed that a reference exists on snode coming into this routine.
795  *
796  * od: This means that the name passed in is an on-disk name.
797  */
798 
799 int
800 smb_fsop_rmdir(
801     struct smb_request *sr,
802     cred_t *cr,
803     smb_node_t *dir_snode,
804     char *name,
805     int od)
806 {
807 	caller_context_t ct;
808 	int rc;
809 	int flags = 0;
810 	char *longname;
811 
812 	ASSERT(cr);
813 	/*
814 	 * The state of the node could be SMB_NODE_STATE_DESTROYING if this
815 	 * function is called during the deletion of the node (because of
816 	 * DELETE_ON_CLOSE).
817 	 */
818 	ASSERT(dir_snode);
819 	ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC);
820 
821 	if (SMB_TREE_ROOT_FS(sr, dir_snode) == 0)
822 		return (EACCES);
823 
824 	if (SMB_TREE_IS_READ_ONLY(sr))
825 		return (EROFS);
826 
827 	/*
828 	 * If the passed-in name is an on-disk name,
829 	 * then we need to do a case-sensitive rmdir.
830 	 * This is important if the on-disk name
831 	 * corresponds to a mangled name passed in by
832 	 * the client.  We want to make sure to remove
833 	 * the exact directory specified by the client,
834 	 * instead of letting the underlying file system
835 	 * do a rmdir on the "first match."
836 	 */
837 
838 	if ((od == 0) && SMB_TREE_CASE_INSENSITIVE(sr))
839 		flags = SMB_IGNORE_CASE;
840 
841 	smb_get_caller_context(sr, &ct);
842 
843 	rc = smb_vop_rmdir(dir_snode->vp, name, flags, cr, &ct);
844 
845 	if (rc == ENOENT) {
846 		if (smb_maybe_mangled_name(name) == 0)
847 			return (rc);
848 
849 		longname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
850 
851 		rc = smb_unmangle_name(sr, cr, dir_snode,
852 		    name, longname, MAXNAMELEN, NULL,
853 		    NULL, 1);
854 
855 		if (rc == 0) {
856 			/*
857 			 * We passed "1" as the "od" parameter
858 			 * to smb_unmangle_name(), such that longname
859 			 * is the real (case-sensitive) on-disk name.
860 			 * We make sure we do a rmdir on this exact
861 			 * name, as the name was mangled and denotes
862 			 * a unique directory.
863 			 */
864 			flags &= ~SMB_IGNORE_CASE;
865 			rc = smb_vop_rmdir(dir_snode->vp, longname, flags, cr,
866 			    &ct);
867 		}
868 
869 		kmem_free(longname, MAXNAMELEN);
870 	}
871 
872 	return (rc);
873 }
874 
875 /*
876  * smb_fsop_getattr
877  *
878  * All SMB functions should use this wrapper to ensure that
879  * the the calls are performed with the appropriate credentials.
880  * Please document any direct call to explain the reason
881  * for avoiding this wrapper.
882  *
883  * It is assumed that a reference exists on snode coming into this routine.
884  */
885 int
886 smb_fsop_getattr(struct smb_request *sr, cred_t *cr, smb_node_t *snode,
887     smb_attr_t *attr)
888 {
889 	smb_node_t *unnamed_node;
890 	vnode_t *unnamed_vp = NULL;
891 	caller_context_t ct;
892 	uint32_t status;
893 	uint32_t access = 0;
894 	int flags = 0;
895 
896 	ASSERT(cr);
897 	ASSERT(snode);
898 	ASSERT(snode->n_magic == SMB_NODE_MAGIC);
899 	ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING);
900 
901 	if (SMB_TREE_ROOT_FS(sr, snode) == 0)
902 		return (EACCES);
903 
904 	if (sr->fid_ofile) {
905 		/* if uid and/or gid is requested */
906 		if (attr->sa_mask & (SMB_AT_UID|SMB_AT_GID))
907 			access |= READ_CONTROL;
908 
909 		/* if anything else is also requested */
910 		if (attr->sa_mask & ~(SMB_AT_UID|SMB_AT_GID))
911 			access |= FILE_READ_ATTRIBUTES;
912 
913 		status = smb_ofile_access(sr->fid_ofile, cr, access);
914 		if (status != NT_STATUS_SUCCESS)
915 			return (EACCES);
916 
917 		if (sr->tid_tree->t_flags & SMB_TREE_FLAG_ACEMASKONACCESS)
918 			flags = ATTR_NOACLCHECK;
919 	}
920 
921 	smb_get_caller_context(sr, &ct);
922 
923 	unnamed_node = SMB_IS_STREAM(snode);
924 
925 	if (unnamed_node) {
926 		ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC);
927 		ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING);
928 		unnamed_vp = unnamed_node->vp;
929 	}
930 
931 	return (smb_vop_getattr(snode->vp, unnamed_vp, attr, flags, cr, &ct));
932 }
933 
934 /*
935  * smb_fsop_readdir
936  *
937  * All SMB functions should use this smb_fsop_readdir wrapper to ensure that
938  * the smb_vop_readdir is performed with the appropriate credentials.
939  * Please document any direct call to smb_vop_readdir to explain the reason
940  * for avoiding this wrapper.
941  *
942  * It is assumed that a reference exists on snode coming into this routine.
943  */
944 int
945 smb_fsop_readdir(
946     struct smb_request *sr,
947     cred_t *cr,
948     smb_node_t *dir_snode,
949     uint32_t *cookie,
950     char *name,
951     int *namelen,
952     ino64_t *fileid,
953     struct fs_stream_info *stream_info,
954     smb_node_t **ret_snode,
955     smb_attr_t *ret_attr)
956 {
957 	caller_context_t ct;
958 	smb_node_t *ret_snodep;
959 	smb_node_t *fnode;
960 	smb_attr_t tmp_attr;
961 	vnode_t *xattrdirvp;
962 	vnode_t *fvp;
963 	vnode_t *vp = NULL;
964 	char *od_name;
965 	int rc;
966 	int flags = 0;
967 
968 	ASSERT(cr);
969 	ASSERT(dir_snode);
970 	ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC);
971 	ASSERT(dir_snode->n_state != SMB_NODE_STATE_DESTROYING);
972 
973 	if (SMB_TREE_ROOT_FS(sr, dir_snode) == 0)
974 		return (EACCES);
975 
976 	if (*cookie == SMB_EOF) {
977 		*namelen = 0;
978 		return (0);
979 	}
980 
981 	if (SMB_TREE_CASE_INSENSITIVE(sr))
982 		flags = SMB_IGNORE_CASE;
983 
984 	smb_get_caller_context(sr, &ct);
985 
986 	od_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
987 
988 	if (stream_info) {
989 		rc = smb_vop_lookup(dir_snode->vp, name, &fvp, od_name,
990 		    SMB_FOLLOW_LINKS, sr->tid_tree->t_snode->vp, cr, &ct);
991 
992 		if (rc != 0) {
993 			kmem_free(od_name, MAXNAMELEN);
994 			return (rc);
995 		}
996 
997 		fnode = smb_node_lookup(sr, NULL, cr, fvp, od_name, dir_snode,
998 		    NULL, ret_attr);
999 
1000 		kmem_free(od_name, MAXNAMELEN);
1001 
1002 		if (fnode == NULL) {
1003 			VN_RELE(fvp);
1004 			return (ENOMEM);
1005 		}
1006 
1007 		/*
1008 		 * XXX
1009 		 * Need to find out what permission(s) NTFS requires for getting
1010 		 * a file's streams list.
1011 		 *
1012 		 * Might have to use kcred.
1013 		 */
1014 		rc = smb_vop_stream_readdir(fvp, cookie, stream_info, &vp,
1015 		    &xattrdirvp, flags, cr, &ct);
1016 
1017 		if ((rc != 0) || (*cookie == SMB_EOF)) {
1018 			smb_node_release(fnode);
1019 			return (rc);
1020 		}
1021 
1022 		ret_snodep = smb_stream_node_lookup(sr, cr, fnode, xattrdirvp,
1023 		    vp, stream_info->name, &tmp_attr);
1024 
1025 		smb_node_release(fnode);
1026 
1027 		if (ret_snodep == NULL) {
1028 			VN_RELE(xattrdirvp);
1029 			VN_RELE(vp);
1030 			return (ENOMEM);
1031 		}
1032 
1033 		stream_info->size = tmp_attr.sa_vattr.va_size;
1034 
1035 		if (ret_attr)
1036 			*ret_attr = tmp_attr;
1037 
1038 		if (ret_snode)
1039 			*ret_snode = ret_snodep;
1040 		else
1041 			smb_node_release(ret_snodep);
1042 
1043 	} else {
1044 		rc = smb_vop_readdir(dir_snode->vp, cookie, name, namelen,
1045 		    fileid, &vp, od_name, flags, cr, &ct);
1046 
1047 		if (rc != 0) {
1048 			kmem_free(od_name, MAXNAMELEN);
1049 			return (rc);
1050 		}
1051 
1052 		if (*namelen) {
1053 			ASSERT(vp);
1054 			if (ret_attr || ret_snode) {
1055 				ret_snodep = smb_node_lookup(sr, NULL, cr, vp,
1056 				    od_name, dir_snode, NULL, &tmp_attr);
1057 
1058 				if (ret_snodep == NULL) {
1059 					kmem_free(od_name, MAXNAMELEN);
1060 					VN_RELE(vp);
1061 					return (ENOMEM);
1062 				}
1063 
1064 				if (ret_attr)
1065 					*ret_attr = tmp_attr;
1066 
1067 				if (ret_snode)
1068 					*ret_snode = ret_snodep;
1069 				else
1070 					smb_node_release(ret_snodep);
1071 			}
1072 		}
1073 
1074 		kmem_free(od_name, MAXNAMELEN);
1075 	}
1076 
1077 	return (rc);
1078 }
1079 
1080 /*
1081  * smb_fsop_getdents
1082  *
1083  * All SMB functions should use this smb_vop_getdents wrapper to ensure that
1084  * the smb_vop_getdents is performed with the appropriate credentials.
1085  * Please document any direct call to smb_vop_getdents to explain the reason
1086  * for avoiding this wrapper.
1087  *
1088  * It is assumed that a reference exists on snode coming into this routine.
1089  */
1090 /*ARGSUSED*/
1091 int
1092 smb_fsop_getdents(
1093     struct smb_request *sr,
1094     cred_t *cr,
1095     smb_node_t *dir_snode,
1096     uint32_t *cookie,
1097     uint64_t *verifierp,
1098     int32_t	*maxcnt,
1099     char *args,
1100     char *pattern)
1101 {
1102 	caller_context_t ct;
1103 	int flags = 0;
1104 
1105 	ASSERT(cr);
1106 	ASSERT(dir_snode);
1107 	ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC);
1108 	ASSERT(dir_snode->n_state != SMB_NODE_STATE_DESTROYING);
1109 
1110 	if (SMB_TREE_ROOT_FS(sr, dir_snode) == 0)
1111 		return (EACCES);
1112 
1113 	if (SMB_TREE_CASE_INSENSITIVE(sr))
1114 		flags = SMB_IGNORE_CASE;
1115 
1116 	smb_get_caller_context(sr, &ct);
1117 
1118 	return (smb_vop_getdents(dir_snode, cookie, 0, maxcnt, args, pattern,
1119 	    flags, sr, cr, &ct));
1120 }
1121 
1122 /*
1123  * smb_fsop_rename
1124  *
1125  * All SMB functions should use this smb_vop_rename wrapper to ensure that
1126  * the smb_vop_rename is performed with the appropriate credentials.
1127  * Please document any direct call to smb_vop_rename to explain the reason
1128  * for avoiding this wrapper.
1129  *
1130  * It is assumed that references exist on from_dir_snode and to_dir_snode coming
1131  * into this routine.
1132  */
1133 int
1134 smb_fsop_rename(
1135     struct smb_request *sr,
1136     cred_t *cr,
1137     smb_node_t *from_dir_snode,
1138     char *from_name,
1139     smb_node_t *to_dir_snode,
1140     char *to_name)
1141 {
1142 	smb_node_t *from_snode;
1143 	caller_context_t ct;
1144 	smb_attr_t tmp_attr;
1145 	vnode_t *from_vp;
1146 	int flags = 0;
1147 	int rc;
1148 
1149 	ASSERT(cr);
1150 	ASSERT(from_dir_snode);
1151 	ASSERT(from_dir_snode->n_magic == SMB_NODE_MAGIC);
1152 	ASSERT(from_dir_snode->n_state != SMB_NODE_STATE_DESTROYING);
1153 
1154 	ASSERT(to_dir_snode);
1155 	ASSERT(to_dir_snode->n_magic == SMB_NODE_MAGIC);
1156 	ASSERT(to_dir_snode->n_state != SMB_NODE_STATE_DESTROYING);
1157 
1158 	if (SMB_TREE_ROOT_FS(sr, from_dir_snode) == 0)
1159 		return (EACCES);
1160 
1161 	if (SMB_TREE_ROOT_FS(sr, to_dir_snode) == 0)
1162 		return (EACCES);
1163 
1164 	ASSERT(sr);
1165 	ASSERT(sr->tid_tree);
1166 	if (SMB_TREE_IS_READ_ONLY(sr))
1167 		return (EROFS);
1168 
1169 	/*
1170 	 * Note: There is no need to check SMB_TREE_CASE_INSENSITIVE(sr)
1171 	 * here.
1172 	 *
1173 	 * A case-sensitive rename is always done in this routine
1174 	 * because we are using the on-disk name from an earlier lookup.
1175 	 * If a mangled name was passed in by the caller (denoting a
1176 	 * deterministic lookup), then the exact file must be renamed
1177 	 * (i.e. SMB_IGNORE_CASE must not be passed to VOP_RENAME, or
1178 	 * else the underlying file system might return a "first-match"
1179 	 * on this on-disk name, possibly resulting in the wrong file).
1180 	 */
1181 
1182 	/*
1183 	 * XXX: Lock required through smb_node_release() below?
1184 	 */
1185 
1186 	smb_get_caller_context(sr, &ct);
1187 
1188 	rc = smb_vop_lookup(from_dir_snode->vp, from_name, &from_vp, NULL, 0,
1189 	    NULL, cr, &ct);
1190 
1191 	if (rc != 0)
1192 		return (rc);
1193 
1194 	rc = smb_vop_rename(from_dir_snode->vp, from_name, to_dir_snode->vp,
1195 	    to_name, flags, cr, &ct);
1196 
1197 	if (rc == 0) {
1198 		from_snode = smb_node_lookup(sr, NULL, cr, from_vp, from_name,
1199 		    from_dir_snode, NULL, &tmp_attr);
1200 
1201 		if (from_snode == NULL) {
1202 			VN_RELE(from_vp);
1203 			return (ENOMEM);
1204 		}
1205 
1206 		(void) smb_node_rename(from_dir_snode, from_snode, to_dir_snode,
1207 		    to_name);
1208 
1209 		smb_node_release(from_snode);
1210 	} else {
1211 		VN_RELE(from_vp);
1212 	}
1213 
1214 	/* XXX: unlock */
1215 
1216 	return (rc);
1217 }
1218 
1219 /*
1220  * smb_fsop_setattr
1221  *
1222  * All SMB functions should use this wrapper to ensure that
1223  * the the calls are performed with the appropriate credentials.
1224  * Please document any direct call to explain the reason
1225  * for avoiding this wrapper.
1226  *
1227  * It is assumed that a reference exists on snode coming into this routine.
1228  * A null smb_request might be passed to this function.
1229  */
1230 int
1231 smb_fsop_setattr(
1232     smb_request_t *sr,
1233     cred_t *cr,
1234     smb_node_t *snode,
1235     smb_attr_t *set_attr,
1236     smb_attr_t *ret_attr)
1237 {
1238 	smb_node_t *unnamed_node;
1239 	vnode_t *unnamed_vp = NULL;
1240 	caller_context_t ct;
1241 	uint32_t status;
1242 	uint32_t access = 0;
1243 	int rc = 0;
1244 	int flags = 0;
1245 
1246 	ASSERT(cr);
1247 	ASSERT(snode);
1248 	ASSERT(snode->n_magic == SMB_NODE_MAGIC);
1249 	ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING);
1250 
1251 	if (SMB_TREE_ROOT_FS(sr, snode) == 0)
1252 		return (EACCES);
1253 
1254 	if (SMB_TREE_IS_READ_ONLY(sr))
1255 		return (EROFS);
1256 
1257 	/* sr could be NULL in some cases */
1258 	if (sr && sr->fid_ofile) {
1259 		/* if uid and/or gid is requested */
1260 		if (set_attr->sa_mask & (SMB_AT_UID|SMB_AT_GID))
1261 			access |= WRITE_OWNER;
1262 
1263 		/* if anything else is also requested */
1264 		if (set_attr->sa_mask & ~(SMB_AT_UID|SMB_AT_GID))
1265 			access |= FILE_WRITE_ATTRIBUTES;
1266 
1267 		status = smb_ofile_access(sr->fid_ofile, cr, access);
1268 		if (status != NT_STATUS_SUCCESS)
1269 			return (EACCES);
1270 
1271 		if (sr->tid_tree->t_flags & SMB_TREE_FLAG_ACEMASKONACCESS)
1272 			flags = ATTR_NOACLCHECK;
1273 	}
1274 
1275 	smb_get_caller_context(sr, &ct);
1276 
1277 	unnamed_node = SMB_IS_STREAM(snode);
1278 
1279 	if (unnamed_node) {
1280 		ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC);
1281 		ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING);
1282 		unnamed_vp = unnamed_node->vp;
1283 	}
1284 
1285 	rc = smb_vop_setattr(snode->vp, unnamed_vp, set_attr, flags, cr, &ct);
1286 
1287 	if ((rc == 0) && ret_attr) {
1288 		/*
1289 		 * This is an operation on behalf of CIFS service (to update
1290 		 * smb node's attr) not on behalf of the user so it's done
1291 		 * using kcred and the return value is intentionally ignored.
1292 		 */
1293 		ret_attr->sa_mask = SMB_AT_ALL;
1294 		(void) smb_vop_getattr(snode->vp, unnamed_vp, ret_attr, 0,
1295 		    kcred, &ct);
1296 	}
1297 
1298 	return (rc);
1299 }
1300 
1301 /*
1302  * smb_fsop_read
1303  *
1304  * All SMB functions should use this wrapper to ensure that
1305  * the the calls are performed with the appropriate credentials.
1306  * Please document any direct call to explain the reason
1307  * for avoiding this wrapper.
1308  *
1309  * It is assumed that a reference exists on snode coming into this routine.
1310  */
1311 int
1312 smb_fsop_read(
1313     struct smb_request *sr,
1314     cred_t *cr,
1315     smb_node_t *snode,
1316     uio_t *uio,
1317     smb_attr_t *ret_attr)
1318 {
1319 	smb_node_t *unnamed_node;
1320 	vnode_t *unnamed_vp = NULL;
1321 	caller_context_t ct;
1322 	int rc;
1323 
1324 	ASSERT(cr);
1325 	ASSERT(snode);
1326 	ASSERT(snode->n_magic == SMB_NODE_MAGIC);
1327 	ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING);
1328 
1329 	ASSERT(sr);
1330 	ASSERT(sr->fid_ofile);
1331 
1332 	rc = smb_ofile_access(sr->fid_ofile, cr, FILE_READ_DATA);
1333 	if (rc != NT_STATUS_SUCCESS) {
1334 		rc = smb_ofile_access(sr->fid_ofile, cr, FILE_EXECUTE);
1335 		if (rc != NT_STATUS_SUCCESS)
1336 			return (EACCES);
1337 	}
1338 
1339 	unnamed_node = SMB_IS_STREAM(snode);
1340 	if (unnamed_node) {
1341 		ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC);
1342 		ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING);
1343 		unnamed_vp = unnamed_node->vp;
1344 		/*
1345 		 * Streams permission are checked against the unnamed stream,
1346 		 * but in FS level they have their own permissions. To avoid
1347 		 * rejection by FS due to lack of permission on the actual
1348 		 * extended attr kcred is passed for streams.
1349 		 */
1350 		cr = kcred;
1351 	}
1352 
1353 	smb_get_caller_context(sr, &ct);
1354 	rc = smb_vop_read(snode->vp, uio, cr, &ct);
1355 
1356 	if (rc == 0) {
1357 		/*
1358 		 * This is an operation on behalf of CIFS service (to update
1359 		 * smb node's attr) not on behalf of the user so it's done
1360 		 * using kcred and the return value is intentionally ignored.
1361 		 */
1362 		ret_attr->sa_mask = SMB_AT_ALL;
1363 		(void) smb_vop_getattr(snode->vp, unnamed_vp, ret_attr, 0,
1364 		    kcred, &ct);
1365 	}
1366 
1367 	return (rc);
1368 }
1369 
1370 /*
1371  * smb_fsop_write
1372  *
1373  * This is a wrapper function used for smb_write and smb_write_raw operations.
1374  *
1375  * It is assumed that a reference exists on snode coming into this routine.
1376  */
1377 int
1378 smb_fsop_write(
1379     struct smb_request *sr,
1380     cred_t *cr,
1381     smb_node_t *snode,
1382     uio_t *uio,
1383     uint32_t *lcount,
1384     smb_attr_t *ret_attr,
1385     uint32_t *flag)
1386 {
1387 	smb_node_t *unnamed_node;
1388 	vnode_t *unnamed_vp = NULL;
1389 	caller_context_t ct;
1390 	int rc;
1391 
1392 	ASSERT(cr);
1393 	ASSERT(snode);
1394 	ASSERT(snode->n_magic == SMB_NODE_MAGIC);
1395 	ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING);
1396 
1397 	ASSERT(sr);
1398 	ASSERT(sr->tid_tree);
1399 	ASSERT(sr->fid_ofile);
1400 
1401 	if (SMB_TREE_IS_READ_ONLY(sr))
1402 		return (EROFS);
1403 	/*
1404 	 * XXX what if the file has been opened only with
1405 	 * FILE_APPEND_DATA?
1406 	 */
1407 	rc = smb_ofile_access(sr->fid_ofile, cr, FILE_WRITE_DATA);
1408 	if (rc != NT_STATUS_SUCCESS)
1409 		return (EACCES);
1410 
1411 	smb_get_caller_context(sr, &ct);
1412 
1413 	unnamed_node = SMB_IS_STREAM(snode);
1414 
1415 	if (unnamed_node) {
1416 		ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC);
1417 		ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING);
1418 		unnamed_vp = unnamed_node->vp;
1419 		/*
1420 		 * Streams permission are checked against the unnamed stream,
1421 		 * but in FS level they have their own permissions. To avoid
1422 		 * rejection by FS due to lack of permission on the actual
1423 		 * extended attr kcred is passed for streams.
1424 		 */
1425 		cr = kcred;
1426 	}
1427 
1428 	rc = smb_vop_write(snode->vp, uio, flag, lcount, cr, &ct);
1429 
1430 	if (rc == 0) {
1431 		/*
1432 		 * This is an operation on behalf of CIFS service (to update
1433 		 * smb node's attr) not on behalf of the user so it's done
1434 		 * using kcred and the return value is intentionally ignored.
1435 		 */
1436 		ret_attr->sa_mask = SMB_AT_ALL;
1437 		(void) smb_vop_getattr(snode->vp, unnamed_vp, ret_attr, 0,
1438 		    kcred, &ct);
1439 	}
1440 
1441 	return (rc);
1442 }
1443 
1444 /*
1445  * smb_fsop_statfs
1446  *
1447  * This is a wrapper function used for stat operations.
1448  */
1449 int
1450 smb_fsop_statfs(
1451     cred_t *cr,
1452     smb_node_t *snode,
1453     struct statvfs64 *statp)
1454 {
1455 	ASSERT(cr);
1456 	ASSERT(snode);
1457 	ASSERT(snode->n_magic == SMB_NODE_MAGIC);
1458 	ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING);
1459 
1460 	return (smb_vop_statfs(snode->vp, statp, cr));
1461 }
1462 
1463 /*
1464  * smb_fsop_access
1465  */
1466 int
1467 smb_fsop_access(smb_request_t *sr, cred_t *cr, smb_node_t *snode,
1468     uint32_t faccess)
1469 {
1470 	int access = 0;
1471 	int error;
1472 	vnode_t *dir_vp;
1473 	boolean_t acl_check = B_TRUE;
1474 	smb_node_t *unnamed_node;
1475 
1476 	ASSERT(cr);
1477 	ASSERT(snode);
1478 	ASSERT(snode->n_magic == SMB_NODE_MAGIC);
1479 	ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING);
1480 
1481 	if (faccess == 0)
1482 		return (NT_STATUS_SUCCESS);
1483 
1484 	if (SMB_TREE_IS_READ_ONLY(sr)) {
1485 		if (faccess & (FILE_WRITE_DATA|FILE_APPEND_DATA|
1486 		    FILE_WRITE_EA|FILE_DELETE_CHILD|FILE_WRITE_ATTRIBUTES|
1487 		    DELETE|WRITE_DAC|WRITE_OWNER)) {
1488 			return (NT_STATUS_ACCESS_DENIED);
1489 		}
1490 	}
1491 
1492 	unnamed_node = SMB_IS_STREAM(snode);
1493 	if (unnamed_node) {
1494 		ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC);
1495 		ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING);
1496 		/*
1497 		 * Streams authorization should be performed against the
1498 		 * unnamed stream.
1499 		 */
1500 		snode = unnamed_node;
1501 	}
1502 
1503 	if (faccess & ACCESS_SYSTEM_SECURITY) {
1504 		/*
1505 		 * This permission is required for reading/writing SACL and
1506 		 * it's not part of DACL. It's only granted via proper
1507 		 * privileges.
1508 		 */
1509 		if ((sr->uid_user->u_privileges &
1510 		    (SMB_USER_PRIV_BACKUP |
1511 		    SMB_USER_PRIV_RESTORE |
1512 		    SMB_USER_PRIV_SECURITY)) == 0)
1513 			return (NT_STATUS_PRIVILEGE_NOT_HELD);
1514 
1515 		faccess &= ~ACCESS_SYSTEM_SECURITY;
1516 	}
1517 
1518 	/* Links don't have ACL */
1519 	if (((sr->tid_tree->t_flags & SMB_TREE_FLAG_ACEMASKONACCESS) == 0) ||
1520 	    (snode->attr.sa_vattr.va_type == VLNK))
1521 		acl_check = B_FALSE;
1522 
1523 	if (acl_check) {
1524 		dir_vp = (snode->dir_snode) ? snode->dir_snode->vp : NULL;
1525 		error = smb_vop_access(snode->vp, faccess, V_ACE_MASK, dir_vp,
1526 		    cr);
1527 	} else {
1528 		/*
1529 		 * FS doesn't understand 32-bit mask, need to map
1530 		 */
1531 		if (faccess & (FILE_WRITE_DATA | FILE_APPEND_DATA))
1532 			access |= VWRITE;
1533 
1534 		if (faccess & FILE_READ_DATA)
1535 			access |= VREAD;
1536 
1537 		if (faccess & FILE_EXECUTE)
1538 			access |= VEXEC;
1539 
1540 		error = smb_vop_access(snode->vp, access, 0, NULL, cr);
1541 	}
1542 
1543 	return ((error) ? NT_STATUS_ACCESS_DENIED : NT_STATUS_SUCCESS);
1544 }
1545 
1546 /*
1547  * smb_fsop_lookup_name()
1548  *
1549  * Sanity checks on dir_snode done in smb_fsop_lookup().
1550  *
1551  * Note: This function is called only from the open path.
1552  * It will check if the file is a stream.
1553  * It will also return an error if the looked-up file is in
1554  * a child mount.
1555  */
1556 
1557 int
1558 smb_fsop_lookup_name(
1559     struct smb_request *sr,
1560     cred_t	*cr,
1561     int		flags,
1562     smb_node_t	*root_node,
1563     smb_node_t	*dir_snode,
1564     char	*name,
1565     smb_node_t	**ret_snode,
1566     smb_attr_t	*ret_attr)
1567 {
1568 	smb_node_t *fnode;
1569 	smb_attr_t file_attr;
1570 	caller_context_t ct;
1571 	vnode_t *xattrdirvp;
1572 	vnode_t *vp;
1573 	char *od_name;
1574 	char *fname;
1575 	char *sname;
1576 	int rc;
1577 
1578 	ASSERT(cr);
1579 	ASSERT(dir_snode);
1580 	ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC);
1581 	ASSERT(dir_snode->n_state != SMB_NODE_STATE_DESTROYING);
1582 
1583 	/*
1584 	 * The following check is required for streams processing, below
1585 	 */
1586 
1587 	if (SMB_TREE_CASE_INSENSITIVE(sr))
1588 		flags |= SMB_IGNORE_CASE;
1589 
1590 	fname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
1591 	sname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
1592 
1593 	if (smb_stream_parse_name(name, fname, sname)) {
1594 		/*
1595 		 * Look up the unnamed stream (i.e. fname).
1596 		 * Unmangle processing will be done on fname
1597 		 * as well as any link target.
1598 		 */
1599 		rc = smb_fsop_lookup(sr, cr, flags, root_node, dir_snode, fname,
1600 		    &fnode, &file_attr, NULL, NULL);
1601 
1602 		if (rc != 0) {
1603 			kmem_free(fname, MAXNAMELEN);
1604 			kmem_free(sname, MAXNAMELEN);
1605 			return (rc);
1606 		}
1607 
1608 		od_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
1609 
1610 		/*
1611 		 * od_name is the on-disk name of the stream, except
1612 		 * without the prepended stream prefix (SMB_STREAM_PREFIX)
1613 		 */
1614 
1615 		/*
1616 		 * XXX
1617 		 * What permissions NTFS requires for stream lookup if any?
1618 		 */
1619 		rc = smb_vop_stream_lookup(fnode->vp, sname, &vp, od_name,
1620 		    &xattrdirvp, flags, root_node->vp, cr, &ct);
1621 
1622 		if (rc != 0) {
1623 			smb_node_release(fnode);
1624 			kmem_free(fname, MAXNAMELEN);
1625 			kmem_free(sname, MAXNAMELEN);
1626 			kmem_free(od_name, MAXNAMELEN);
1627 			return (rc);
1628 		}
1629 
1630 		*ret_snode = smb_stream_node_lookup(sr, cr, fnode, xattrdirvp,
1631 		    vp, od_name, ret_attr);
1632 
1633 		kmem_free(od_name, MAXNAMELEN);
1634 		smb_node_release(fnode);
1635 
1636 		if (*ret_snode == NULL) {
1637 			VN_RELE(xattrdirvp);
1638 			VN_RELE(vp);
1639 			kmem_free(fname, MAXNAMELEN);
1640 			kmem_free(sname, MAXNAMELEN);
1641 			return (ENOMEM);
1642 		}
1643 	} else {
1644 		rc = smb_fsop_lookup(sr, cr, flags, root_node, dir_snode, name,
1645 		    ret_snode, ret_attr, NULL, NULL);
1646 	}
1647 
1648 	if (rc == 0) {
1649 		ASSERT(ret_snode);
1650 		if (SMB_TREE_ROOT_FS(sr, *ret_snode) == 0) {
1651 			smb_node_release(*ret_snode);
1652 			*ret_snode = NULL;
1653 			rc = EACCES;
1654 		}
1655 	}
1656 
1657 	kmem_free(fname, MAXNAMELEN);
1658 	kmem_free(sname, MAXNAMELEN);
1659 
1660 	return (rc);
1661 }
1662 
1663 /*
1664  * smb_fsop_lookup
1665  *
1666  * All SMB functions should use this smb_vop_lookup wrapper to ensure that
1667  * the smb_vop_lookup is performed with the appropriate credentials and using
1668  * case insensitive compares. Please document any direct call to smb_vop_lookup
1669  * to explain the reason for avoiding this wrapper.
1670  *
1671  * It is assumed that a reference exists on dir_snode coming into this routine
1672  * (and that it is safe from deallocation).
1673  *
1674  * Same with the root_node.
1675  *
1676  * *ret_snode is returned with a reference upon success.  No reference is
1677  * taken if an error is returned.
1678  *
1679  * Note: The returned ret_snode may be in a child mount.  This is ok for
1680  * readdir and getdents.
1681  *
1682  * Other smb_fsop_* routines will call SMB_TREE_ROOT_FS() to prevent
1683  * operations on files not in the parent mount.
1684  */
1685 int
1686 smb_fsop_lookup(
1687     struct smb_request *sr,
1688     cred_t	*cr,
1689     int		flags,
1690     smb_node_t	*root_node,
1691     smb_node_t	*dir_snode,
1692     char	*name,
1693     smb_node_t	**ret_snode,
1694     smb_attr_t	*ret_attr,
1695     char	*ret_shortname, /* Must be at least MANGLE_NAMELEN chars */
1696     char	*ret_name83)    /* Must be at least MANGLE_NAMELEN chars */
1697 {
1698 	smb_node_t *lnk_target_node;
1699 	smb_node_t *lnk_dnode;
1700 	caller_context_t ct;
1701 	char *longname;
1702 	char *od_name;
1703 	vnode_t *vp;
1704 	int rc;
1705 
1706 	ASSERT(cr);
1707 	ASSERT(dir_snode);
1708 	ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC);
1709 	ASSERT(dir_snode->n_state != SMB_NODE_STATE_DESTROYING);
1710 
1711 	if (name == NULL)
1712 		return (EINVAL);
1713 
1714 	if (SMB_TREE_ROOT_FS(sr, dir_snode) == 0)
1715 		return (EACCES);
1716 
1717 	if (SMB_TREE_CASE_INSENSITIVE(sr))
1718 		flags |= SMB_IGNORE_CASE;
1719 
1720 	smb_get_caller_context(sr, &ct);
1721 
1722 	od_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
1723 
1724 	rc = smb_vop_lookup(dir_snode->vp, name, &vp, od_name, flags,
1725 	    root_node ? root_node->vp : NULL, cr, &ct);
1726 
1727 	if (rc != 0) {
1728 		if (smb_maybe_mangled_name(name) == 0) {
1729 			kmem_free(od_name, MAXNAMELEN);
1730 			return (rc);
1731 		}
1732 
1733 		longname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
1734 
1735 		rc = smb_unmangle_name(sr, cr, dir_snode, name, longname,
1736 		    MAXNAMELEN, ret_shortname, ret_name83, 1);
1737 
1738 		if (rc != 0) {
1739 			kmem_free(od_name, MAXNAMELEN);
1740 			kmem_free(longname, MAXNAMELEN);
1741 			return (rc);
1742 		}
1743 
1744 		/*
1745 		 * We passed "1" as the "od" parameter
1746 		 * to smb_unmangle_name(), such that longname
1747 		 * is the real (case-sensitive) on-disk name.
1748 		 * We make sure we do a lookup on this exact
1749 		 * name, as the name was mangled and denotes
1750 		 * a unique file.
1751 		 */
1752 
1753 		if (flags & SMB_IGNORE_CASE)
1754 			flags &= ~SMB_IGNORE_CASE;
1755 
1756 		rc = smb_vop_lookup(dir_snode->vp, longname, &vp, od_name,
1757 		    flags, root_node ? root_node->vp : NULL, cr, &ct);
1758 
1759 		kmem_free(longname, MAXNAMELEN);
1760 
1761 		if (rc != 0) {
1762 			kmem_free(od_name, MAXNAMELEN);
1763 			return (rc);
1764 		}
1765 	}
1766 
1767 	if ((flags & SMB_FOLLOW_LINKS) && (vp->v_type == VLNK)) {
1768 
1769 		rc = smb_pathname(sr, od_name, FOLLOW, root_node, dir_snode,
1770 		    &lnk_dnode, &lnk_target_node, cr);
1771 
1772 		if (rc != 0) {
1773 			/*
1774 			 * The link is assumed to be for the last component
1775 			 * of a path.  Hence any ENOTDIR error will be returned
1776 			 * as ENOENT.
1777 			 */
1778 			if (rc == ENOTDIR)
1779 				rc = ENOENT;
1780 
1781 			VN_RELE(vp);
1782 			kmem_free(od_name, MAXNAMELEN);
1783 			return (rc);
1784 		}
1785 
1786 		/*
1787 		 * Release the original VLNK vnode
1788 		 */
1789 
1790 		VN_RELE(vp);
1791 		vp = lnk_target_node->vp;
1792 
1793 		rc = smb_vop_traverse_check(&vp);
1794 
1795 		if (rc != 0) {
1796 			smb_node_release(lnk_dnode);
1797 			smb_node_release(lnk_target_node);
1798 			kmem_free(od_name, MAXNAMELEN);
1799 			return (rc);
1800 		}
1801 
1802 		/*
1803 		 * smb_vop_traverse_check() may have returned a different vnode
1804 		 */
1805 
1806 		if (lnk_target_node->vp == vp) {
1807 			*ret_snode = lnk_target_node;
1808 			*ret_attr = (*ret_snode)->attr;
1809 		} else {
1810 			*ret_snode = smb_node_lookup(sr, NULL, cr, vp,
1811 			    lnk_target_node->od_name, lnk_dnode, NULL,
1812 			    ret_attr);
1813 
1814 			if (*ret_snode == NULL) {
1815 				VN_RELE(vp);
1816 				rc = ENOMEM;
1817 			}
1818 			smb_node_release(lnk_target_node);
1819 		}
1820 
1821 		smb_node_release(lnk_dnode);
1822 
1823 	} else {
1824 
1825 		rc = smb_vop_traverse_check(&vp);
1826 		if (rc) {
1827 			VN_RELE(vp);
1828 			kmem_free(od_name, MAXNAMELEN);
1829 			return (rc);
1830 		}
1831 
1832 		*ret_snode = smb_node_lookup(sr, NULL, cr, vp, od_name,
1833 		    dir_snode, NULL, ret_attr);
1834 
1835 		if (*ret_snode == NULL) {
1836 			VN_RELE(vp);
1837 			rc = ENOMEM;
1838 		}
1839 	}
1840 
1841 	kmem_free(od_name, MAXNAMELEN);
1842 	return (rc);
1843 }
1844 
1845 /*
1846  * smb_fsop_stream_readdir()
1847  *
1848  * ret_snode and ret_attr are optional parameters (i.e. NULL may be passed in)
1849  *
1850  * This routine will return only NTFS streams.  If an NTFS stream is not
1851  * found at the offset specified, the directory will be read until an NTFS
1852  * stream is found or until EOF.
1853  *
1854  * Note: Sanity checks done in caller
1855  * (smb_fsop_readdir(), smb_fsop_remove_streams())
1856  */
1857 
1858 int
1859 smb_fsop_stream_readdir(struct smb_request *sr, cred_t *cr, smb_node_t *fnode,
1860     uint32_t *cookiep, struct fs_stream_info *stream_info,
1861     smb_node_t **ret_snode, smb_attr_t *ret_attr)
1862 {
1863 	smb_node_t *ret_snodep = NULL;
1864 	caller_context_t ct;
1865 	smb_attr_t tmp_attr;
1866 	vnode_t *xattrdirvp;
1867 	vnode_t *vp;
1868 	int rc = 0;
1869 	int flags = 0;
1870 
1871 	/*
1872 	 * XXX NTFS permission requirements if any?
1873 	 */
1874 	ASSERT(cr);
1875 	ASSERT(fnode);
1876 	ASSERT(fnode->n_magic == SMB_NODE_MAGIC);
1877 	ASSERT(fnode->n_state != SMB_NODE_STATE_DESTROYING);
1878 
1879 	if (SMB_TREE_CASE_INSENSITIVE(sr))
1880 		flags = SMB_IGNORE_CASE;
1881 
1882 	smb_get_caller_context(sr, &ct);
1883 
1884 	rc = smb_vop_stream_readdir(fnode->vp, cookiep, stream_info, &vp,
1885 	    &xattrdirvp, flags, cr, &ct);
1886 
1887 	if ((rc != 0) || *cookiep == SMB_EOF)
1888 		return (rc);
1889 
1890 	ret_snodep = smb_stream_node_lookup(sr, cr, fnode, xattrdirvp, vp,
1891 	    stream_info->name, &tmp_attr);
1892 
1893 	if (ret_snodep == NULL) {
1894 		VN_RELE(xattrdirvp);
1895 		VN_RELE(vp);
1896 		return (ENOMEM);
1897 	}
1898 
1899 	stream_info->size = tmp_attr.sa_vattr.va_size;
1900 
1901 	if (ret_attr)
1902 		*ret_attr = tmp_attr;
1903 
1904 	if (ret_snode)
1905 		*ret_snode = ret_snodep;
1906 	else
1907 		smb_node_release(ret_snodep);
1908 
1909 	return (rc);
1910 }
1911 
1912 int /*ARGSUSED*/
1913 smb_fsop_commit(smb_request_t *sr, cred_t *cr, smb_node_t *snode)
1914 {
1915 	caller_context_t ct;
1916 
1917 	ASSERT(cr);
1918 	ASSERT(snode);
1919 	ASSERT(snode->n_magic == SMB_NODE_MAGIC);
1920 	ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING);
1921 
1922 	ASSERT(sr);
1923 	ASSERT(sr->tid_tree);
1924 	if (SMB_TREE_IS_READ_ONLY(sr))
1925 		return (EROFS);
1926 
1927 	smb_get_caller_context(sr, &ct);
1928 
1929 	return (smb_vop_commit(snode->vp, cr, &ct));
1930 }
1931 
1932 /*
1933  * smb_fsop_sdinit
1934  *
1935  * Initializes the given FS SD structure.
1936  */
1937 void
1938 smb_fsop_sdinit(smb_fssd_t *fs_sd, uint32_t secinfo, uint32_t flags)
1939 {
1940 	bzero(fs_sd, sizeof (smb_fssd_t));
1941 	fs_sd->sd_secinfo = secinfo;
1942 	fs_sd->sd_flags = flags;
1943 }
1944 
1945 /*
1946  * smb_fsop_sdterm
1947  *
1948  * Frees allocated memory for acl fields.
1949  */
1950 void
1951 smb_fsop_sdterm(smb_fssd_t *fs_sd)
1952 {
1953 	ASSERT(fs_sd);
1954 
1955 	smb_fsop_aclfree(fs_sd->sd_zdacl);
1956 	smb_fsop_aclfree(fs_sd->sd_zsacl);
1957 	bzero(fs_sd, sizeof (smb_fssd_t));
1958 }
1959 
1960 /*
1961  * smb_fsop_aclread
1962  *
1963  * Retrieve filesystem ACL. Depends on requested ACLs in
1964  * fs_sd->sd_secinfo, it'll set DACL and SACL pointers in
1965  * fs_sd. Note that requesting a DACL/SACL doesn't mean that
1966  * the corresponding field in fs_sd should be non-NULL upon
1967  * return, since the target ACL might not contain that type of
1968  * entries.
1969  *
1970  * Returned ACL is always in ACE_T (aka ZFS) format.
1971  * If successful the allocated memory for the ACL should be freed
1972  * using smb_fsop_aclfree() or smb_fsop_sdterm()
1973  */
1974 int
1975 smb_fsop_aclread(smb_request_t *sr, cred_t *cr, smb_node_t *snode,
1976     smb_fssd_t *fs_sd)
1977 {
1978 	int error = 0;
1979 	int flags = 0;
1980 	int access = 0;
1981 	acl_t *acl;
1982 	caller_context_t ct;
1983 	smb_node_t *unnamed_node;
1984 
1985 	ASSERT(cr);
1986 
1987 	if (sr->fid_ofile) {
1988 		if (fs_sd->sd_secinfo & SMB_DACL_SECINFO)
1989 			access = READ_CONTROL;
1990 
1991 		if (fs_sd->sd_secinfo & SMB_SACL_SECINFO)
1992 			access |= ACCESS_SYSTEM_SECURITY;
1993 
1994 		error = smb_ofile_access(sr->fid_ofile, cr, access);
1995 		if (error != NT_STATUS_SUCCESS) {
1996 			return (EACCES);
1997 		}
1998 	}
1999 
2000 	unnamed_node = SMB_IS_STREAM(snode);
2001 	if (unnamed_node) {
2002 		ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC);
2003 		ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING);
2004 		/*
2005 		 * Streams don't have ACL, any read ACL attempt on a stream
2006 		 * should be performed on the unnamed stream.
2007 		 */
2008 		snode = unnamed_node;
2009 	}
2010 
2011 	if (sr->tid_tree->t_flags & SMB_TREE_FLAG_ACEMASKONACCESS)
2012 		flags = ATTR_NOACLCHECK;
2013 
2014 	smb_get_caller_context(sr, &ct);
2015 	error = smb_vop_acl_read(snode->vp, &acl, flags,
2016 	    sr->tid_tree->t_acltype, cr, &ct);
2017 	if (error != 0) {
2018 		return (error);
2019 	}
2020 
2021 	error = acl_translate(acl, _ACL_ACE_ENABLED,
2022 	    (snode->vp->v_type == VDIR), fs_sd->sd_uid, fs_sd->sd_gid);
2023 
2024 	if (error == 0) {
2025 		smb_fsop_aclsplit(acl, &fs_sd->sd_zdacl, &fs_sd->sd_zsacl,
2026 		    fs_sd->sd_secinfo);
2027 	}
2028 
2029 	acl_free(acl);
2030 	return (error);
2031 }
2032 
2033 /*
2034  * smb_fsop_aclwrite
2035  *
2036  * Stores the filesystem ACL provided in fs_sd->sd_acl.
2037  */
2038 int
2039 smb_fsop_aclwrite(smb_request_t *sr, cred_t *cr, smb_node_t *snode,
2040     smb_fssd_t *fs_sd)
2041 {
2042 	int target_flavor;
2043 	int error = 0;
2044 	int flags = 0;
2045 	int access = 0;
2046 	caller_context_t ct;
2047 	acl_t *acl, *dacl, *sacl;
2048 	smb_node_t *unnamed_node;
2049 
2050 	ASSERT(cr);
2051 
2052 	ASSERT(sr);
2053 	ASSERT(sr->tid_tree);
2054 	if (SMB_TREE_IS_READ_ONLY(sr))
2055 		return (EROFS);
2056 
2057 	if (sr->fid_ofile) {
2058 		if (fs_sd->sd_secinfo & SMB_DACL_SECINFO)
2059 			access = WRITE_DAC;
2060 
2061 		if (fs_sd->sd_secinfo & SMB_SACL_SECINFO)
2062 			access |= ACCESS_SYSTEM_SECURITY;
2063 
2064 		error = smb_ofile_access(sr->fid_ofile, cr, access);
2065 		if (error != NT_STATUS_SUCCESS)
2066 			return (EACCES);
2067 	}
2068 
2069 	switch (sr->tid_tree->t_acltype) {
2070 	case ACLENT_T:
2071 		target_flavor = _ACL_ACLENT_ENABLED;
2072 		break;
2073 
2074 	case ACE_T:
2075 		target_flavor = _ACL_ACE_ENABLED;
2076 		break;
2077 	default:
2078 		return (EINVAL);
2079 	}
2080 
2081 	unnamed_node = SMB_IS_STREAM(snode);
2082 	if (unnamed_node) {
2083 		ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC);
2084 		ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING);
2085 		/*
2086 		 * Streams don't have ACL, any write ACL attempt on a stream
2087 		 * should be performed on the unnamed stream.
2088 		 */
2089 		snode = unnamed_node;
2090 	}
2091 
2092 	dacl = fs_sd->sd_zdacl;
2093 	sacl = fs_sd->sd_zsacl;
2094 
2095 	ASSERT(dacl || sacl);
2096 	if ((dacl == NULL) && (sacl == NULL))
2097 		return (EINVAL);
2098 
2099 	if (dacl && sacl)
2100 		acl = smb_fsop_aclmerge(dacl, sacl);
2101 	else if (dacl)
2102 		acl = dacl;
2103 	else
2104 		acl = sacl;
2105 
2106 	error = acl_translate(acl, target_flavor, (snode->vp->v_type == VDIR),
2107 	    fs_sd->sd_uid, fs_sd->sd_gid);
2108 	if (error == 0) {
2109 		smb_get_caller_context(sr, &ct);
2110 		if (sr->tid_tree->t_flags & SMB_TREE_FLAG_ACEMASKONACCESS)
2111 			flags = ATTR_NOACLCHECK;
2112 
2113 		error = smb_vop_acl_write(snode->vp, acl, flags, cr, &ct);
2114 	}
2115 
2116 	if (dacl && sacl)
2117 		acl_free(acl);
2118 
2119 	return (error);
2120 }
2121 
2122 acl_t *
2123 smb_fsop_aclalloc(int acenum, int flags)
2124 {
2125 	acl_t *acl;
2126 
2127 	acl = acl_alloc(ACE_T);
2128 	acl->acl_cnt = acenum;
2129 	acl->acl_aclp = kmem_zalloc(acl->acl_entry_size * acenum, KM_SLEEP);
2130 	acl->acl_flags = flags;
2131 	return (acl);
2132 }
2133 
2134 void
2135 smb_fsop_aclfree(acl_t *acl)
2136 {
2137 	if (acl)
2138 		acl_free(acl);
2139 }
2140 
2141 /*
2142  * smb_fsop_aclmerge
2143  *
2144  * smb_fsop_aclread/write routines which interact with filesystem
2145  * work with single ACL. This routine merges given DACL and SACL
2146  * which might have been created during CIFS to FS conversion into
2147  * one single ACL.
2148  */
2149 static acl_t *
2150 smb_fsop_aclmerge(acl_t *dacl, acl_t *sacl)
2151 {
2152 	acl_t *acl;
2153 	int dacl_size;
2154 
2155 	ASSERT(dacl);
2156 	ASSERT(sacl);
2157 
2158 	acl = smb_fsop_aclalloc(dacl->acl_cnt + sacl->acl_cnt, dacl->acl_flags);
2159 	dacl_size = dacl->acl_cnt * dacl->acl_entry_size;
2160 	bcopy(dacl->acl_aclp, acl->acl_aclp, dacl_size);
2161 	bcopy(sacl->acl_aclp, (char *)acl->acl_aclp + dacl_size,
2162 	    sacl->acl_cnt * sacl->acl_entry_size);
2163 
2164 	return (acl);
2165 }
2166 
2167 /*
2168  * smb_fsop_aclsplit
2169  *
2170  * splits the given ACE_T ACL (zacl) to one or two ACLs (DACL/SACL) based on
2171  * the 'which_acl' parameter. Note that output dacl/sacl parameters could be
2172  * NULL even if they're specified in 'which_acl', which means the target
2173  * doesn't have any access and/or audit ACEs.
2174  */
2175 static void
2176 smb_fsop_aclsplit(acl_t *zacl, acl_t **dacl, acl_t **sacl, int which_acl)
2177 {
2178 	ace_t *zace;
2179 	ace_t *access_ace;
2180 	ace_t *audit_ace;
2181 	int naccess, naudit;
2182 	int get_dacl, get_sacl;
2183 	int i;
2184 
2185 	*dacl = *sacl = NULL;
2186 	naccess = naudit = 0;
2187 	get_dacl = (which_acl & SMB_DACL_SECINFO);
2188 	get_sacl = (which_acl & SMB_SACL_SECINFO);
2189 
2190 	for (i = 0, zace = zacl->acl_aclp; i < zacl->acl_cnt; zace++, i++) {
2191 		if (get_dacl && smb_ace_is_access(zace->a_type))
2192 			naccess++;
2193 		else if (get_sacl && smb_ace_is_audit(zace->a_type))
2194 			naudit++;
2195 	}
2196 
2197 	if (naccess) {
2198 		*dacl = smb_fsop_aclalloc(naccess, zacl->acl_flags);
2199 		access_ace = (*dacl)->acl_aclp;
2200 	}
2201 
2202 	if (naudit) {
2203 		*sacl = smb_fsop_aclalloc(naudit, zacl->acl_flags);
2204 		audit_ace = (*sacl)->acl_aclp;
2205 	}
2206 
2207 	for (i = 0, zace = zacl->acl_aclp; i < zacl->acl_cnt; zace++, i++) {
2208 		if (get_dacl && smb_ace_is_access(zace->a_type)) {
2209 			*access_ace = *zace;
2210 			access_ace++;
2211 		} else if (get_sacl && smb_ace_is_audit(zace->a_type)) {
2212 			*audit_ace = *zace;
2213 			audit_ace++;
2214 		}
2215 	}
2216 }
2217 
2218 acl_type_t
2219 smb_fsop_acltype(smb_node_t *snode)
2220 {
2221 	return (smb_vop_acl_type(snode->vp));
2222 }
2223 
2224 /*
2225  * smb_fsop_sdread
2226  *
2227  * Read the requested security descriptor items from filesystem.
2228  * The items are specified in fs_sd->sd_secinfo.
2229  */
2230 int
2231 smb_fsop_sdread(smb_request_t *sr, cred_t *cr, smb_node_t *snode,
2232     smb_fssd_t *fs_sd)
2233 {
2234 	int error = 0;
2235 	int getowner = 0;
2236 	cred_t *ga_cred;
2237 	smb_attr_t attr;
2238 
2239 	ASSERT(cr);
2240 	ASSERT(fs_sd);
2241 
2242 	/*
2243 	 * File's uid/gid is fetched in two cases:
2244 	 *
2245 	 * 1. it's explicitly requested
2246 	 *
2247 	 * 2. target ACL is ACE_T (ZFS ACL). They're needed for
2248 	 *    owner@/group@ entries. In this case kcred should be used
2249 	 *    because uid/gid are fetched on behalf of smb server.
2250 	 */
2251 	if (fs_sd->sd_secinfo & (SMB_OWNER_SECINFO | SMB_GROUP_SECINFO)) {
2252 		getowner = 1;
2253 		ga_cred = cr;
2254 	} else if (sr->tid_tree->t_acltype == ACE_T) {
2255 		getowner = 1;
2256 		ga_cred = kcred;
2257 	}
2258 
2259 	if (getowner) {
2260 		/*
2261 		 * Windows require READ_CONTROL to read owner/group SID since
2262 		 * they're part of Security Descriptor.
2263 		 * ZFS only requires read_attribute. Need to have a explicit
2264 		 * access check here.
2265 		 */
2266 		if (sr->fid_ofile == NULL) {
2267 			error = smb_fsop_access(sr, ga_cred, snode,
2268 			    READ_CONTROL);
2269 			if (error)
2270 				return (error);
2271 		}
2272 
2273 		attr.sa_mask = SMB_AT_UID | SMB_AT_GID;
2274 		error = smb_fsop_getattr(sr, ga_cred, snode, &attr);
2275 		if (error == 0) {
2276 			fs_sd->sd_uid = attr.sa_vattr.va_uid;
2277 			fs_sd->sd_gid = attr.sa_vattr.va_gid;
2278 		} else {
2279 			return (error);
2280 		}
2281 	}
2282 
2283 	if (fs_sd->sd_secinfo & SMB_ACL_SECINFO) {
2284 		error = smb_fsop_aclread(sr, cr, snode, fs_sd);
2285 	}
2286 
2287 	return (error);
2288 }
2289 
2290 /*
2291  * smb_fsop_sdmerge
2292  *
2293  * From SMB point of view DACL and SACL are two separate list
2294  * which can be manipulated independently without one affecting
2295  * the other, but entries for both DACL and SACL will end up
2296  * in the same ACL if target filesystem supports ACE_T ACLs.
2297  *
2298  * So, if either DACL or SACL is present in the client set request
2299  * the entries corresponding to the non-present ACL shouldn't
2300  * be touched in the FS ACL.
2301  *
2302  * fs_sd parameter contains DACL and SACL specified by SMB
2303  * client to be set on a file/directory. The client could
2304  * specify both or one of these ACLs (if none is specified
2305  * we don't get this far). When both DACL and SACL are given
2306  * by client the existing ACL should be overwritten. If only
2307  * one of them is specified the entries corresponding to the other
2308  * ACL should not be touched. For example, if only DACL
2309  * is specified in input fs_sd, the function reads audit entries
2310  * of the existing ACL of the file and point fs_sd->sd_zsdacl
2311  * pointer to the fetched SACL, this way when smb_fsop_sdwrite()
2312  * function is called the passed fs_sd would point to the specified
2313  * DACL by client and fetched SACL from filesystem, so the file
2314  * will end up with correct ACL.
2315  */
2316 static int
2317 smb_fsop_sdmerge(smb_request_t *sr, smb_node_t *snode, smb_fssd_t *fs_sd)
2318 {
2319 	smb_fssd_t cur_sd;
2320 	int error = 0;
2321 
2322 	if (sr->tid_tree->t_acltype != ACE_T)
2323 		/* Don't bother if target FS doesn't support ACE_T */
2324 		return (0);
2325 
2326 	if ((fs_sd->sd_secinfo & SMB_ACL_SECINFO) != SMB_ACL_SECINFO) {
2327 		if (fs_sd->sd_secinfo & SMB_DACL_SECINFO) {
2328 			/*
2329 			 * Don't overwrite existing audit entries
2330 			 */
2331 			smb_fsop_sdinit(&cur_sd, SMB_SACL_SECINFO,
2332 			    fs_sd->sd_flags);
2333 
2334 			error = smb_fsop_sdread(sr, kcred, snode, &cur_sd);
2335 			if (error == 0) {
2336 				ASSERT(fs_sd->sd_zsacl == NULL);
2337 				fs_sd->sd_zsacl = cur_sd.sd_zsacl;
2338 				if (fs_sd->sd_zsacl && fs_sd->sd_zdacl)
2339 					fs_sd->sd_zsacl->acl_flags =
2340 					    fs_sd->sd_zdacl->acl_flags;
2341 			}
2342 		} else {
2343 			/*
2344 			 * Don't overwrite existing access entries
2345 			 */
2346 			smb_fsop_sdinit(&cur_sd, SMB_DACL_SECINFO,
2347 			    fs_sd->sd_flags);
2348 
2349 			error = smb_fsop_sdread(sr, kcred, snode, &cur_sd);
2350 			if (error == 0) {
2351 				ASSERT(fs_sd->sd_zdacl == NULL);
2352 				fs_sd->sd_zdacl = cur_sd.sd_zdacl;
2353 				if (fs_sd->sd_zdacl && fs_sd->sd_zsacl)
2354 					fs_sd->sd_zdacl->acl_flags =
2355 					    fs_sd->sd_zsacl->acl_flags;
2356 			}
2357 		}
2358 
2359 		if (error)
2360 			smb_fsop_sdterm(&cur_sd);
2361 	}
2362 
2363 	return (error);
2364 }
2365 
2366 /*
2367  * smb_fsop_sdwrite
2368  *
2369  * Stores the given uid, gid and acl in filesystem.
2370  * Provided items in fs_sd are specified by fs_sd->sd_secinfo.
2371  *
2372  * A SMB security descriptor could contain owner, primary group,
2373  * DACL and SACL. Setting an SD should be atomic but here it has to
2374  * be done via two separate FS operations: VOP_SETATTR and
2375  * VOP_SETSECATTR. Therefore, this function has to simulate the
2376  * atomicity as well as it can.
2377  */
2378 int
2379 smb_fsop_sdwrite(smb_request_t *sr, cred_t *cr, smb_node_t *snode,
2380     smb_fssd_t *fs_sd, int overwrite)
2381 {
2382 	int error = 0;
2383 	int access = 0;
2384 	smb_attr_t set_attr;
2385 	smb_attr_t orig_attr;
2386 
2387 	ASSERT(cr);
2388 	ASSERT(fs_sd);
2389 
2390 	ASSERT(sr);
2391 	ASSERT(sr->tid_tree);
2392 	if (SMB_TREE_IS_READ_ONLY(sr))
2393 		return (EROFS);
2394 
2395 	bzero(&set_attr, sizeof (smb_attr_t));
2396 
2397 	if (fs_sd->sd_secinfo & SMB_OWNER_SECINFO) {
2398 		set_attr.sa_vattr.va_uid = fs_sd->sd_uid;
2399 		set_attr.sa_mask |= SMB_AT_UID;
2400 	}
2401 
2402 	if (fs_sd->sd_secinfo & SMB_GROUP_SECINFO) {
2403 		set_attr.sa_vattr.va_gid = fs_sd->sd_gid;
2404 		set_attr.sa_mask |= SMB_AT_GID;
2405 	}
2406 
2407 	if (fs_sd->sd_secinfo & SMB_DACL_SECINFO)
2408 		access |= WRITE_DAC;
2409 
2410 	if (fs_sd->sd_secinfo & SMB_SACL_SECINFO)
2411 		access |= ACCESS_SYSTEM_SECURITY;
2412 
2413 	if (sr->fid_ofile)
2414 		error = smb_ofile_access(sr->fid_ofile, cr, access);
2415 	else
2416 		error = smb_fsop_access(sr, cr, snode, access);
2417 
2418 	if (error)
2419 		return (EACCES);
2420 
2421 	if (set_attr.sa_mask) {
2422 		/*
2423 		 * Get the current uid, gid so if smb_fsop_aclwrite fails
2424 		 * we can revert uid, gid changes.
2425 		 *
2426 		 * We use root cred here so the operation doesn't fail
2427 		 * due to lack of permission for the user to read the attrs
2428 		 */
2429 
2430 		orig_attr.sa_mask = SMB_AT_UID | SMB_AT_GID;
2431 		error = smb_fsop_getattr(sr, kcred, snode, &orig_attr);
2432 		if (error == 0)
2433 			error = smb_fsop_setattr(sr, cr, snode, &set_attr,
2434 			    NULL);
2435 
2436 		if (error)
2437 			return (error);
2438 	}
2439 
2440 	if (fs_sd->sd_secinfo & SMB_ACL_SECINFO) {
2441 		if (overwrite == 0) {
2442 			error = smb_fsop_sdmerge(sr, snode, fs_sd);
2443 			if (error)
2444 				return (error);
2445 		}
2446 
2447 		error = smb_fsop_aclwrite(sr, cr, snode, fs_sd);
2448 		if (error) {
2449 			/*
2450 			 * Revert uid/gid changes if required.
2451 			 */
2452 			if (set_attr.sa_mask) {
2453 				orig_attr.sa_mask = set_attr.sa_mask;
2454 				(void) smb_fsop_setattr(sr, kcred, snode,
2455 				    &orig_attr, NULL);
2456 			}
2457 		}
2458 	}
2459 
2460 	return (error);
2461 }
2462 
2463 /*ARGSUSED*/
2464 void
2465 smb_get_caller_context(smb_request_t *sr, caller_context_t *ct)
2466 {
2467 	ct->cc_caller_id = smb_caller_id;
2468 	ct->cc_pid = 0;			/* TBD */
2469 	ct->cc_sysid = 0;		/* TBD */
2470 }
2471 
2472 /*
2473  * smb_fsop_sdinherit
2474  *
2475  * Inherit the security descriptor from the parent container.
2476  * This function is called after FS has created the file/folder
2477  * so if this doesn't do anything it means FS inheritance is
2478  * in place.
2479  *
2480  * Do inheritance for ZFS internally.
2481  *
2482  * If we want to let ZFS does the inheritance the
2483  * following setting should be true:
2484  *
2485  *  - aclinherit = passthrough
2486  *  - aclmode = passthrough
2487  *  - smbd umask = 0777
2488  *
2489  * This will result in right effective permissions but
2490  * ZFS will always add 6 ACEs for owner, owning group
2491  * and others to be POSIX compliant. This is not what
2492  * Windows clients/users expect, so we decided that CIFS
2493  * implements Windows rules and overwrite whatever ZFS
2494  * comes up with. This way we also don't have to care
2495  * about ZFS aclinherit and aclmode settings.
2496  */
2497 static int
2498 smb_fsop_sdinherit(smb_request_t *sr, smb_node_t *dnode, smb_fssd_t *fs_sd)
2499 {
2500 	int is_dir;
2501 	acl_t *dacl;
2502 	acl_t *sacl;
2503 	ksid_t *owner_sid;
2504 	int error;
2505 
2506 	ASSERT(fs_sd);
2507 
2508 	if (sr->tid_tree->t_acltype != ACE_T) {
2509 		/*
2510 		 * No forced inheritance for non-ZFS filesystems.
2511 		 */
2512 		fs_sd->sd_secinfo = 0;
2513 		return (0);
2514 	}
2515 
2516 
2517 	/* Fetch parent directory's ACL */
2518 	error = smb_fsop_sdread(sr, kcred, dnode, fs_sd);
2519 	if (error) {
2520 		return (error);
2521 	}
2522 
2523 	is_dir = (fs_sd->sd_flags & SMB_FSSD_FLAGS_DIR);
2524 	owner_sid = crgetsid(sr->user_cr, KSID_OWNER);
2525 	ASSERT(owner_sid);
2526 	dacl = smb_acl_inherit(fs_sd->sd_zdacl, is_dir, SMB_DACL_SECINFO,
2527 	    owner_sid->ks_id);
2528 	sacl = smb_acl_inherit(fs_sd->sd_zsacl, is_dir, SMB_SACL_SECINFO,
2529 	    (uid_t)-1);
2530 
2531 	smb_fsop_aclfree(fs_sd->sd_zdacl);
2532 	smb_fsop_aclfree(fs_sd->sd_zsacl);
2533 
2534 	fs_sd->sd_zdacl = dacl;
2535 	fs_sd->sd_zsacl = sacl;
2536 
2537 	return (0);
2538 }
2539 
2540 /*
2541  * smb_fsop_eaccess
2542  *
2543  * Returns the effective permission of the given credential for the
2544  * specified object.
2545  *
2546  * This is just a workaround. We need VFS/FS support for this.
2547  */
2548 void
2549 smb_fsop_eaccess(smb_request_t *sr, cred_t *cr, smb_node_t *snode,
2550     uint32_t *eaccess)
2551 {
2552 	int access = 0;
2553 	vnode_t *dir_vp;
2554 	smb_node_t *unnamed_node;
2555 
2556 	ASSERT(cr);
2557 	ASSERT(snode);
2558 	ASSERT(snode->n_magic == SMB_NODE_MAGIC);
2559 	ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING);
2560 
2561 	unnamed_node = SMB_IS_STREAM(snode);
2562 	if (unnamed_node) {
2563 		ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC);
2564 		ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING);
2565 		/*
2566 		 * Streams authorization should be performed against the
2567 		 * unnamed stream.
2568 		 */
2569 		snode = unnamed_node;
2570 	}
2571 
2572 	if (sr->tid_tree->t_flags & SMB_TREE_FLAG_ACEMASKONACCESS) {
2573 		dir_vp = (snode->dir_snode) ? snode->dir_snode->vp : NULL;
2574 		smb_vop_eaccess(snode->vp, (int *)eaccess, V_ACE_MASK, dir_vp,
2575 		    cr);
2576 		return;
2577 	}
2578 
2579 	/*
2580 	 * FS doesn't understand 32-bit mask
2581 	 */
2582 	smb_vop_eaccess(snode->vp, &access, 0, NULL, cr);
2583 
2584 	*eaccess = READ_CONTROL | FILE_READ_EA | FILE_READ_ATTRIBUTES;
2585 
2586 	if (access & VREAD)
2587 		*eaccess |= FILE_READ_DATA;
2588 
2589 	if (access & VEXEC)
2590 		*eaccess |= FILE_EXECUTE;
2591 
2592 	if (access & VWRITE)
2593 		*eaccess |= FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES |
2594 		    FILE_WRITE_EA | FILE_APPEND_DATA | FILE_DELETE_CHILD;
2595 }
2596