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