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