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