xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_fsops.c (revision 6cefaae1e90a413ba01560575bb3998e1a3df40e)
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 #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 				VN_RELE(vp);
241 				rc = ENOMEM;
242 			}
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 			VN_RELE(vp);
275 			rc = ENOMEM;
276 		}
277 	}
278 
279 	if (rc != 0) {
280 		if (is_dir)
281 			(void) smb_vop_rmdir(dir_snode->vp, name, flags, cr);
282 		else
283 			(void) smb_vop_remove(dir_snode->vp, name, flags, cr);
284 	}
285 
286 	return (rc);
287 }
288 
289 /*
290  * smb_fsop_create
291  *
292  * All SMB functions should use this wrapper to ensure that
293  * all the smb_vop_creates are performed with the appropriate credentials.
294  * Please document any direct calls to explain the reason
295  * for avoiding this wrapper.
296  *
297  * It is assumed that a reference exists on snode coming into this routine.
298  *
299  * *ret_snode is returned with a reference upon success.  No reference is
300  * taken if an error is returned.
301  */
302 
303 int
304 smb_fsop_create(
305     smb_request_t	*sr,
306     cred_t		*cr,
307     smb_node_t		*dir_snode,
308     char		*name,
309     smb_attr_t		*attr,
310     smb_node_t		**ret_snode,
311     smb_attr_t		*ret_attr)
312 {
313 	struct open_param *op = &sr->arg.open;
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 		attr->sa_vattr.va_uid = file_attr.sa_vattr.va_uid;
421 		attr->sa_vattr.va_gid = file_attr.sa_vattr.va_gid;
422 		attr->sa_mask = SMB_AT_UID | SMB_AT_GID;
423 
424 		/*
425 		 * The second parameter of smb_vop_setattr() is set to
426 		 * NULL, even though an unnamed stream exists.  This is
427 		 * because we want to set the UID and GID on the named
428 		 * stream in this case for consistency with the (unnamed
429 		 * stream) file (see comments for smb_vop_setattr()).
430 		 */
431 
432 		rc = smb_vop_setattr(vp, NULL, attr, 0, kcred);
433 
434 		if (rc != 0) {
435 			smb_node_release(fnode);
436 			kmem_free(fname, MAXNAMELEN);
437 			kmem_free(sname, MAXNAMELEN);
438 			return (rc);
439 		}
440 
441 		*ret_snode = smb_stream_node_lookup(sr, cr, fnode, xattrdirvp,
442 		    vp, sname, ret_attr);
443 
444 		smb_node_release(fnode);
445 
446 		if (*ret_snode == NULL) {
447 			VN_RELE(xattrdirvp);
448 			VN_RELE(vp);
449 			kmem_free(fname, MAXNAMELEN);
450 			kmem_free(sname, MAXNAMELEN);
451 			return (ENOMEM);
452 		}
453 	} else {
454 		if (op->sd) {
455 			/*
456 			 * SD sent by client in Windows format. Needs to be
457 			 * converted to FS format. No inheritance.
458 			 */
459 			secinfo = smb_sd_get_secinfo(op->sd);
460 			smb_fssd_init(&fs_sd, secinfo, 0);
461 
462 			status = smb_sd_tofs(op->sd, &fs_sd);
463 			if (status == NT_STATUS_SUCCESS) {
464 				rc = smb_fsop_create_with_sd(sr, cr, dir_snode,
465 				    name, attr, ret_snode, ret_attr, &fs_sd);
466 			}
467 			else
468 				rc = EINVAL;
469 			smb_fssd_term(&fs_sd);
470 		} else if (sr->tid_tree->t_acltype == ACE_T) {
471 			/*
472 			 * No incoming SD and filesystem is ZFS
473 			 * Server applies Windows inheritance rules,
474 			 * see smb_fsop_sdinherit() comments as to why.
475 			 */
476 			smb_fssd_init(&fs_sd, SMB_ACL_SECINFO, 0);
477 			rc = smb_fsop_sdinherit(sr, dir_snode, &fs_sd);
478 			if (rc == 0) {
479 				rc = smb_fsop_create_with_sd(sr, cr, dir_snode,
480 				    name, attr, ret_snode, ret_attr, &fs_sd);
481 			}
482 
483 			smb_fssd_term(&fs_sd);
484 		} else {
485 			/*
486 			 * No incoming SD and filesystem is not ZFS
487 			 * let the filesystem handles the inheritance.
488 			 */
489 			rc = smb_vop_create(dir_snode->vp, name, attr, &vp,
490 			    flags, cr, NULL);
491 
492 			if (rc == 0) {
493 				*ret_snode = smb_node_lookup(sr, op, cr, vp,
494 				    name, dir_snode, NULL, ret_attr);
495 
496 				if (*ret_snode == NULL) {
497 					VN_RELE(vp);
498 					rc = ENOMEM;
499 				}
500 			}
501 
502 		}
503 	}
504 
505 	kmem_free(fname, MAXNAMELEN);
506 	kmem_free(sname, MAXNAMELEN);
507 	return (rc);
508 }
509 
510 /*
511  * smb_fsop_mkdir
512  *
513  * All SMB functions should use this wrapper to ensure that
514  * the the calls are performed with the appropriate credentials.
515  * Please document any direct call to explain the reason
516  * for avoiding this wrapper.
517  *
518  * It is assumed that a reference exists on snode coming into this routine.
519  *
520  * *ret_snode is returned with a reference upon success.  No reference is
521  * taken if an error is returned.
522  */
523 int
524 smb_fsop_mkdir(
525     smb_request_t *sr,
526     cred_t *cr,
527     smb_node_t *dir_snode,
528     char *name,
529     smb_attr_t *attr,
530     smb_node_t **ret_snode,
531     smb_attr_t *ret_attr)
532 {
533 	struct open_param *op = &sr->arg.open;
534 	char *longname;
535 	vnode_t *vp;
536 	int flags = 0;
537 	smb_fssd_t fs_sd;
538 	uint32_t secinfo;
539 	uint32_t status;
540 	int rc;
541 	ASSERT(cr);
542 	ASSERT(dir_snode);
543 	ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC);
544 	ASSERT(dir_snode->n_state != SMB_NODE_STATE_DESTROYING);
545 
546 	ASSERT(ret_snode);
547 	*ret_snode = 0;
548 
549 	ASSERT(name);
550 	if (*name == 0)
551 		return (EINVAL);
552 
553 	ASSERT(sr);
554 	ASSERT(sr->tid_tree);
555 
556 	if (SMB_TREE_CONTAINS_NODE(sr, dir_snode) == 0)
557 		return (EACCES);
558 
559 	if (SMB_TREE_IS_READONLY(sr))
560 		return (EROFS);
561 
562 	if (smb_maybe_mangled_name(name)) {
563 		longname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
564 		rc = smb_unmangle_name(sr, cr, dir_snode, name, longname,
565 		    MAXNAMELEN, NULL, NULL, 1);
566 
567 		kmem_free(longname, MAXNAMELEN);
568 
569 		/*
570 		 * If the name passed in by the client has an unmangled
571 		 * equivalent that is found in the specified directory,
572 		 * then the mkdir cannot succeed.  Return EEXIST.
573 		 *
574 		 * Only if ENOENT is returned will a mkdir be attempted.
575 		 */
576 
577 		if (rc == 0)
578 			rc = EEXIST;
579 
580 		if (rc != ENOENT)
581 			return (rc);
582 	}
583 
584 	if (SMB_TREE_IS_CASEINSENSITIVE(sr))
585 		flags = SMB_IGNORE_CASE;
586 
587 	if (op->sd) {
588 		/*
589 		 * SD sent by client in Windows format. Needs to be
590 		 * converted to FS format. No inheritance.
591 		 */
592 		secinfo = smb_sd_get_secinfo(op->sd);
593 		smb_fssd_init(&fs_sd, secinfo, SMB_FSSD_FLAGS_DIR);
594 
595 		status = smb_sd_tofs(op->sd, &fs_sd);
596 		if (status == NT_STATUS_SUCCESS) {
597 			rc = smb_fsop_create_with_sd(sr, cr, dir_snode,
598 			    name, attr, ret_snode, ret_attr, &fs_sd);
599 		}
600 		else
601 			rc = EINVAL;
602 		smb_fssd_term(&fs_sd);
603 	} else if (sr->tid_tree->t_acltype == ACE_T) {
604 		/*
605 		 * No incoming SD and filesystem is ZFS
606 		 * Server applies Windows inheritance rules,
607 		 * see smb_fsop_sdinherit() comments as to why.
608 		 */
609 		smb_fssd_init(&fs_sd, SMB_ACL_SECINFO, SMB_FSSD_FLAGS_DIR);
610 		rc = smb_fsop_sdinherit(sr, dir_snode, &fs_sd);
611 		if (rc == 0) {
612 			rc = smb_fsop_create_with_sd(sr, cr, dir_snode,
613 			    name, attr, ret_snode, ret_attr, &fs_sd);
614 		}
615 
616 		smb_fssd_term(&fs_sd);
617 
618 	} else {
619 		rc = smb_vop_mkdir(dir_snode->vp, name, attr, &vp, flags, cr,
620 		    NULL);
621 
622 		if (rc == 0) {
623 			*ret_snode = smb_node_lookup(sr, op, cr, vp, name,
624 			    dir_snode, NULL, ret_attr);
625 
626 			if (*ret_snode == NULL) {
627 				VN_RELE(vp);
628 				rc = ENOMEM;
629 			}
630 		}
631 	}
632 
633 	return (rc);
634 }
635 
636 /*
637  * smb_fsop_remove
638  *
639  * All SMB functions should use this wrapper to ensure that
640  * the the calls are performed with the appropriate credentials.
641  * Please document any direct call to explain the reason
642  * for avoiding this wrapper.
643  *
644  * It is assumed that a reference exists on snode coming into this routine.
645  *
646  * od: This means that the name passed in is an on-disk name.
647  * A null smb_request might be passed to this function.
648  */
649 
650 int
651 smb_fsop_remove(
652     smb_request_t	*sr,
653     cred_t		*cr,
654     smb_node_t		*dir_snode,
655     char		*name,
656     int			od)
657 {
658 	smb_node_t	*fnode;
659 	smb_attr_t	file_attr;
660 	char		*longname;
661 	char		*fname;
662 	char		*sname;
663 	int		flags = 0;
664 	int		rc;
665 
666 	ASSERT(cr);
667 	/*
668 	 * The state of the node could be SMB_NODE_STATE_DESTROYING if this
669 	 * function is called during the deletion of the node (because of
670 	 * DELETE_ON_CLOSE).
671 	 */
672 	ASSERT(dir_snode);
673 	ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC);
674 
675 	if (SMB_TREE_CONTAINS_NODE(sr, dir_snode) == 0)
676 		return (EACCES);
677 
678 	if (SMB_TREE_IS_READONLY(sr))
679 		return (EROFS);
680 
681 	fname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
682 	sname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
683 
684 	/*
685 	 * If the passed-in name is an on-disk name,
686 	 * then we need to do a case-sensitive remove.
687 	 * This is important if the on-disk name
688 	 * corresponds to a mangled name passed in by
689 	 * the client.  We want to make sure to remove
690 	 * the exact file specified by the client,
691 	 * instead of letting the underlying file system
692 	 * do a remove on the "first match."
693 	 */
694 
695 	if ((od == 0) && SMB_TREE_IS_CASEINSENSITIVE(sr))
696 		flags = SMB_IGNORE_CASE;
697 
698 	if (dir_snode->flags & NODE_XATTR_DIR) {
699 		rc = smb_vop_stream_remove(dir_snode->dir_snode->vp,
700 		    name, flags, cr);
701 	} else if (smb_stream_parse_name(name, fname, sname)) {
702 		/*
703 		 * It is assumed that "name" corresponds to the path
704 		 * passed in by the client, and no need of suppressing
705 		 * case-insensitive lookups is needed.
706 		 */
707 
708 		ASSERT(od == 0);
709 
710 		/*
711 		 * Look up the unnamed stream (i.e. fname).
712 		 * Unmangle processing will be done on fname
713 		 * as well as any link target.
714 		 */
715 
716 		rc = smb_fsop_lookup(sr, cr, flags | SMB_FOLLOW_LINKS,
717 		    sr->tid_tree->t_snode, dir_snode, fname, &fnode, &file_attr,
718 		    0, 0);
719 
720 		if (rc != 0) {
721 			kmem_free(fname, MAXNAMELEN);
722 			kmem_free(sname, MAXNAMELEN);
723 			return (rc);
724 		}
725 
726 		/*
727 		 * XXX
728 		 * Need to find out what permission is required by NTFS
729 		 * to remove a stream.
730 		 */
731 		rc = smb_vop_stream_remove(fnode->vp, sname, flags, cr);
732 
733 		smb_node_release(fnode);
734 	} else {
735 		rc = smb_vop_remove(dir_snode->vp, name, flags, cr);
736 
737 		if (rc == ENOENT) {
738 			if (smb_maybe_mangled_name(name) == 0) {
739 				kmem_free(fname, MAXNAMELEN);
740 				kmem_free(sname, MAXNAMELEN);
741 				return (rc);
742 			}
743 			longname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
744 
745 			rc = smb_unmangle_name(sr, cr, dir_snode, name,
746 			    longname, MAXNAMELEN, NULL, NULL, 1);
747 
748 			if (rc == 0) {
749 				/*
750 				 * We passed "1" as the "od" parameter
751 				 * to smb_unmangle_name(), such that longname
752 				 * is the real (case-sensitive) on-disk name.
753 				 * We make sure we do a remove on this exact
754 				 * name, as the name was mangled and denotes
755 				 * a unique file.
756 				 */
757 				flags &= ~SMB_IGNORE_CASE;
758 				rc = smb_vop_remove(dir_snode->vp, longname,
759 				    flags, cr);
760 			}
761 
762 			kmem_free(longname, MAXNAMELEN);
763 		}
764 	}
765 
766 	kmem_free(fname, MAXNAMELEN);
767 	kmem_free(sname, MAXNAMELEN);
768 	return (rc);
769 }
770 
771 /*
772  * smb_fsop_remove_streams
773  *
774  * This function removes a file's streams without removing the
775  * file itself.
776  *
777  * It is assumed that snode is not a link.
778  */
779 int
780 smb_fsop_remove_streams(smb_request_t *sr, cred_t *cr, smb_node_t *fnode)
781 {
782 	struct fs_stream_info stream_info;
783 	uint32_t cookie = 0;
784 	int flags = 0;
785 	int rc;
786 
787 	ASSERT(sr);
788 	ASSERT(cr);
789 	ASSERT(fnode);
790 	ASSERT(fnode->n_magic == SMB_NODE_MAGIC);
791 	ASSERT(fnode->n_state != SMB_NODE_STATE_DESTROYING);
792 
793 	if (SMB_TREE_CONTAINS_NODE(sr, fnode) == 0)
794 		return (EACCES);
795 
796 	if (SMB_TREE_IS_READONLY(sr))
797 		return (EROFS);
798 
799 	if (SMB_TREE_IS_CASEINSENSITIVE(sr))
800 		flags = SMB_IGNORE_CASE;
801 
802 	for (;;) {
803 		rc = smb_vop_stream_readdir(fnode->vp, &cookie, &stream_info,
804 		    NULL, NULL, flags, cr);
805 
806 		if ((rc != 0) || (cookie == SMB_EOF))
807 			break;
808 
809 		(void) smb_vop_stream_remove(fnode->vp, stream_info.name, flags,
810 		    cr);
811 	}
812 	return (rc);
813 }
814 
815 /*
816  * smb_fsop_rmdir
817  *
818  * All SMB functions should use this wrapper to ensure that
819  * the the calls are performed with the appropriate credentials.
820  * Please document any direct call to explain the reason
821  * for avoiding this wrapper.
822  *
823  * It is assumed that a reference exists on snode coming into this routine.
824  *
825  * od: This means that the name passed in is an on-disk name.
826  */
827 
828 int
829 smb_fsop_rmdir(
830     smb_request_t	*sr,
831     cred_t		*cr,
832     smb_node_t		*dir_snode,
833     char		*name,
834     int			od)
835 {
836 	int		rc;
837 	int		flags = 0;
838 	char		*longname;
839 
840 	ASSERT(cr);
841 	/*
842 	 * The state of the node could be SMB_NODE_STATE_DESTROYING if this
843 	 * function is called during the deletion of the node (because of
844 	 * DELETE_ON_CLOSE).
845 	 */
846 	ASSERT(dir_snode);
847 	ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC);
848 
849 	if (SMB_TREE_CONTAINS_NODE(sr, dir_snode) == 0)
850 		return (EACCES);
851 
852 	if (SMB_TREE_IS_READONLY(sr))
853 		return (EROFS);
854 
855 	/*
856 	 * If the passed-in name is an on-disk name,
857 	 * then we need to do a case-sensitive rmdir.
858 	 * This is important if the on-disk name
859 	 * corresponds to a mangled name passed in by
860 	 * the client.  We want to make sure to remove
861 	 * the exact directory specified by the client,
862 	 * instead of letting the underlying file system
863 	 * do a rmdir on the "first match."
864 	 */
865 
866 	if ((od == 0) && SMB_TREE_IS_CASEINSENSITIVE(sr))
867 		flags = SMB_IGNORE_CASE;
868 
869 	rc = smb_vop_rmdir(dir_snode->vp, name, flags, cr);
870 
871 	if (rc == ENOENT) {
872 		if (smb_maybe_mangled_name(name) == 0)
873 			return (rc);
874 
875 		longname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
876 
877 		rc = smb_unmangle_name(sr, cr, dir_snode,
878 		    name, longname, MAXNAMELEN, NULL,
879 		    NULL, 1);
880 
881 		if (rc == 0) {
882 			/*
883 			 * We passed "1" as the "od" parameter
884 			 * to smb_unmangle_name(), such that longname
885 			 * is the real (case-sensitive) on-disk name.
886 			 * We make sure we do a rmdir on this exact
887 			 * name, as the name was mangled and denotes
888 			 * a unique directory.
889 			 */
890 			flags &= ~SMB_IGNORE_CASE;
891 			rc = smb_vop_rmdir(dir_snode->vp, longname, flags, cr);
892 		}
893 
894 		kmem_free(longname, MAXNAMELEN);
895 	}
896 
897 	return (rc);
898 }
899 
900 /*
901  * smb_fsop_getattr
902  *
903  * All SMB functions should use this wrapper to ensure that
904  * the the calls are performed with the appropriate credentials.
905  * Please document any direct call to explain the reason
906  * for avoiding this wrapper.
907  *
908  * It is assumed that a reference exists on snode coming into this routine.
909  */
910 int
911 smb_fsop_getattr(smb_request_t *sr, cred_t *cr, smb_node_t *snode,
912     smb_attr_t *attr)
913 {
914 	smb_node_t *unnamed_node;
915 	vnode_t *unnamed_vp = NULL;
916 	uint32_t status;
917 	uint32_t access = 0;
918 	int flags = 0;
919 	int rc;
920 
921 	ASSERT(cr);
922 	ASSERT(snode);
923 	ASSERT(snode->n_magic == SMB_NODE_MAGIC);
924 	ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING);
925 
926 	if (SMB_TREE_CONTAINS_NODE(sr, snode) == 0)
927 		return (EACCES);
928 
929 	if (sr->fid_ofile) {
930 		/* if uid and/or gid is requested */
931 		if (attr->sa_mask & (SMB_AT_UID|SMB_AT_GID))
932 			access |= READ_CONTROL;
933 
934 		/* if anything else is also requested */
935 		if (attr->sa_mask & ~(SMB_AT_UID|SMB_AT_GID))
936 			access |= FILE_READ_ATTRIBUTES;
937 
938 		status = smb_ofile_access(sr->fid_ofile, cr, access);
939 		if (status != NT_STATUS_SUCCESS)
940 			return (EACCES);
941 
942 		if (smb_tree_has_feature(sr->tid_tree,
943 		    SMB_TREE_ACEMASKONACCESS))
944 			flags = ATTR_NOACLCHECK;
945 	}
946 
947 	unnamed_node = SMB_IS_STREAM(snode);
948 
949 	if (unnamed_node) {
950 		ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC);
951 		ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING);
952 		unnamed_vp = unnamed_node->vp;
953 	}
954 
955 	rc = smb_vop_getattr(snode->vp, unnamed_vp, attr, flags, cr);
956 	if (rc == 0)
957 		snode->attr = *attr;
958 
959 	return (rc);
960 }
961 
962 /*
963  * smb_fsop_readdir
964  *
965  * All SMB functions should use this smb_fsop_readdir wrapper to ensure that
966  * the smb_vop_readdir is performed with the appropriate credentials.
967  * Please document any direct call to smb_vop_readdir to explain the reason
968  * for avoiding this wrapper.
969  *
970  * It is assumed that a reference exists on snode coming into this routine.
971  */
972 int
973 smb_fsop_readdir(
974     smb_request_t *sr,
975     cred_t *cr,
976     smb_node_t *dir_snode,
977     uint32_t *cookie,
978     char *name,
979     int *namelen,
980     ino64_t *fileid,
981     struct fs_stream_info *stream_info,
982     smb_node_t **ret_snode,
983     smb_attr_t *ret_attr)
984 {
985 	smb_node_t	*ret_snodep;
986 	smb_node_t	*fnode;
987 	smb_attr_t	tmp_attr;
988 	vnode_t		*xattrdirvp;
989 	vnode_t		*fvp;
990 	vnode_t		*vp = NULL;
991 	char		*od_name;
992 	int		rc;
993 	int		flags = 0;
994 
995 	ASSERT(cr);
996 	ASSERT(dir_snode);
997 	ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC);
998 	ASSERT(dir_snode->n_state != SMB_NODE_STATE_DESTROYING);
999 
1000 	if (SMB_TREE_CONTAINS_NODE(sr, dir_snode) == 0)
1001 		return (EACCES);
1002 
1003 	if (*cookie == SMB_EOF) {
1004 		*namelen = 0;
1005 		return (0);
1006 	}
1007 
1008 	if (SMB_TREE_IS_CASEINSENSITIVE(sr))
1009 		flags = SMB_IGNORE_CASE;
1010 
1011 	od_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
1012 
1013 	if (stream_info) {
1014 		rc = smb_vop_lookup(dir_snode->vp, name, &fvp, od_name,
1015 		    SMB_FOLLOW_LINKS, sr->tid_tree->t_snode->vp, cr);
1016 
1017 		if (rc != 0) {
1018 			kmem_free(od_name, MAXNAMELEN);
1019 			return (rc);
1020 		}
1021 
1022 		fnode = smb_node_lookup(sr, NULL, cr, fvp, od_name, dir_snode,
1023 		    NULL, ret_attr);
1024 
1025 		kmem_free(od_name, MAXNAMELEN);
1026 
1027 		if (fnode == NULL) {
1028 			VN_RELE(fvp);
1029 			return (ENOMEM);
1030 		}
1031 
1032 		/*
1033 		 * XXX
1034 		 * Need to find out what permission(s) NTFS requires for getting
1035 		 * a file's streams list.
1036 		 *
1037 		 * Might have to use kcred.
1038 		 */
1039 		rc = smb_vop_stream_readdir(fvp, cookie, stream_info, &vp,
1040 		    &xattrdirvp, flags, cr);
1041 
1042 		if ((rc != 0) || (*cookie == SMB_EOF)) {
1043 			smb_node_release(fnode);
1044 			return (rc);
1045 		}
1046 
1047 		ret_snodep = smb_stream_node_lookup(sr, cr, fnode, xattrdirvp,
1048 		    vp, stream_info->name, &tmp_attr);
1049 
1050 		smb_node_release(fnode);
1051 
1052 		if (ret_snodep == NULL) {
1053 			VN_RELE(xattrdirvp);
1054 			VN_RELE(vp);
1055 			return (ENOMEM);
1056 		}
1057 
1058 		stream_info->size = tmp_attr.sa_vattr.va_size;
1059 
1060 		if (ret_attr)
1061 			*ret_attr = tmp_attr;
1062 
1063 		if (ret_snode)
1064 			*ret_snode = ret_snodep;
1065 		else
1066 			smb_node_release(ret_snodep);
1067 
1068 	} else {
1069 		rc = smb_vop_readdir(dir_snode->vp, cookie, name, namelen,
1070 		    fileid, &vp, od_name, flags, cr);
1071 
1072 		if (rc != 0) {
1073 			kmem_free(od_name, MAXNAMELEN);
1074 			return (rc);
1075 		}
1076 
1077 		if (*namelen) {
1078 			ASSERT(vp);
1079 			if (ret_attr || ret_snode) {
1080 				ret_snodep = smb_node_lookup(sr, NULL, cr, vp,
1081 				    od_name, dir_snode, NULL, &tmp_attr);
1082 
1083 				if (ret_snodep == NULL) {
1084 					kmem_free(od_name, MAXNAMELEN);
1085 					VN_RELE(vp);
1086 					return (ENOMEM);
1087 				}
1088 
1089 				if (ret_attr)
1090 					*ret_attr = tmp_attr;
1091 
1092 				if (ret_snode)
1093 					*ret_snode = ret_snodep;
1094 				else
1095 					smb_node_release(ret_snodep);
1096 			}
1097 		}
1098 
1099 		kmem_free(od_name, MAXNAMELEN);
1100 	}
1101 
1102 	return (rc);
1103 }
1104 
1105 /*
1106  * smb_fsop_getdents
1107  *
1108  * All SMB functions should use this smb_vop_getdents wrapper to ensure that
1109  * the smb_vop_getdents is performed with the appropriate credentials.
1110  * Please document any direct call to smb_vop_getdents to explain the reason
1111  * for avoiding this wrapper.
1112  *
1113  * It is assumed that a reference exists on snode coming into this routine.
1114  */
1115 /*ARGSUSED*/
1116 int
1117 smb_fsop_getdents(
1118     struct smb_request *sr,
1119     cred_t *cr,
1120     smb_node_t *dir_snode,
1121     uint32_t *cookie,
1122     uint64_t *verifierp,
1123     int32_t	*maxcnt,
1124     char *args,
1125     char *pattern)
1126 {
1127 	int flags = 0;
1128 
1129 	ASSERT(cr);
1130 	ASSERT(dir_snode);
1131 	ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC);
1132 	ASSERT(dir_snode->n_state != SMB_NODE_STATE_DESTROYING);
1133 
1134 	if (SMB_TREE_CONTAINS_NODE(sr, dir_snode) == 0)
1135 		return (EACCES);
1136 
1137 	if (SMB_TREE_IS_CASEINSENSITIVE(sr))
1138 		flags = SMB_IGNORE_CASE;
1139 
1140 	return (smb_vop_getdents(dir_snode, cookie, 0, maxcnt, args, pattern,
1141 	    flags, sr, cr));
1142 }
1143 
1144 /*
1145  * smb_fsop_rename
1146  *
1147  * All SMB functions should use this smb_vop_rename wrapper to ensure that
1148  * the smb_vop_rename is performed with the appropriate credentials.
1149  * Please document any direct call to smb_vop_rename to explain the reason
1150  * for avoiding this wrapper.
1151  *
1152  * It is assumed that references exist on from_dir_snode and to_dir_snode coming
1153  * into this routine.
1154  */
1155 int
1156 smb_fsop_rename(
1157     smb_request_t *sr,
1158     cred_t *cr,
1159     smb_node_t *from_dir_snode,
1160     char *from_name,
1161     smb_node_t *to_dir_snode,
1162     char *to_name)
1163 {
1164 	smb_node_t *from_snode;
1165 	smb_attr_t tmp_attr;
1166 	vnode_t *from_vp;
1167 	int flags = 0;
1168 	int rc;
1169 
1170 	ASSERT(cr);
1171 	ASSERT(from_dir_snode);
1172 	ASSERT(from_dir_snode->n_magic == SMB_NODE_MAGIC);
1173 	ASSERT(from_dir_snode->n_state != SMB_NODE_STATE_DESTROYING);
1174 
1175 	ASSERT(to_dir_snode);
1176 	ASSERT(to_dir_snode->n_magic == SMB_NODE_MAGIC);
1177 	ASSERT(to_dir_snode->n_state != SMB_NODE_STATE_DESTROYING);
1178 
1179 	if (SMB_TREE_CONTAINS_NODE(sr, from_dir_snode) == 0)
1180 		return (EACCES);
1181 
1182 	if (SMB_TREE_CONTAINS_NODE(sr, to_dir_snode) == 0)
1183 		return (EACCES);
1184 
1185 	ASSERT(sr);
1186 	ASSERT(sr->tid_tree);
1187 	if (SMB_TREE_IS_READONLY(sr))
1188 		return (EROFS);
1189 
1190 	/*
1191 	 * Note: There is no need to check SMB_TREE_IS_CASEINSENSITIVE
1192 	 * here.
1193 	 *
1194 	 * A case-sensitive rename is always done in this routine
1195 	 * because we are using the on-disk name from an earlier lookup.
1196 	 * If a mangled name was passed in by the caller (denoting a
1197 	 * deterministic lookup), then the exact file must be renamed
1198 	 * (i.e. SMB_IGNORE_CASE must not be passed to VOP_RENAME, or
1199 	 * else the underlying file system might return a "first-match"
1200 	 * on this on-disk name, possibly resulting in the wrong file).
1201 	 */
1202 
1203 	/*
1204 	 * XXX: Lock required through smb_node_release() below?
1205 	 */
1206 
1207 	rc = smb_vop_lookup(from_dir_snode->vp, from_name, &from_vp, NULL, 0,
1208 	    NULL, cr);
1209 
1210 	if (rc != 0)
1211 		return (rc);
1212 
1213 	rc = smb_vop_rename(from_dir_snode->vp, from_name, to_dir_snode->vp,
1214 	    to_name, flags, cr);
1215 
1216 	if (rc == 0) {
1217 		from_snode = smb_node_lookup(sr, NULL, cr, from_vp, from_name,
1218 		    from_dir_snode, NULL, &tmp_attr);
1219 
1220 		if (from_snode == NULL) {
1221 			VN_RELE(from_vp);
1222 			return (ENOMEM);
1223 		}
1224 
1225 		(void) smb_node_rename(from_dir_snode, from_snode, to_dir_snode,
1226 		    to_name);
1227 
1228 		smb_node_release(from_snode);
1229 	} else {
1230 		VN_RELE(from_vp);
1231 	}
1232 
1233 	/* XXX: unlock */
1234 
1235 	return (rc);
1236 }
1237 
1238 /*
1239  * smb_fsop_setattr
1240  *
1241  * All SMB functions should use this wrapper to ensure that
1242  * the the calls are performed with the appropriate credentials.
1243  * Please document any direct call to explain the reason
1244  * for avoiding this wrapper.
1245  *
1246  * It is assumed that a reference exists on snode coming into this routine.
1247  * A null smb_request might be passed to this function.
1248  */
1249 int
1250 smb_fsop_setattr(
1251     smb_request_t	*sr,
1252     cred_t		*cr,
1253     smb_node_t		*snode,
1254     smb_attr_t		*set_attr,
1255     smb_attr_t		*ret_attr)
1256 {
1257 	smb_node_t *unnamed_node;
1258 	vnode_t *unnamed_vp = NULL;
1259 	uint32_t status;
1260 	uint32_t access;
1261 	int rc = 0;
1262 	int flags = 0;
1263 	uint_t sa_mask;
1264 
1265 	ASSERT(cr);
1266 	ASSERT(snode);
1267 	ASSERT(snode->n_magic == SMB_NODE_MAGIC);
1268 	ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING);
1269 
1270 	if (SMB_TREE_CONTAINS_NODE(sr, snode) == 0)
1271 		return (EACCES);
1272 
1273 	if (SMB_TREE_IS_READONLY(sr))
1274 		return (EROFS);
1275 
1276 	if (sr && (set_attr->sa_mask & SMB_AT_SIZE)) {
1277 		if (sr->fid_ofile) {
1278 			if (SMB_OFILE_IS_READONLY(sr->fid_ofile))
1279 				return (EACCES);
1280 		} else {
1281 			if (SMB_PATHFILE_IS_READONLY(sr, snode))
1282 				return (EACCES);
1283 		}
1284 	}
1285 
1286 	/* sr could be NULL in some cases */
1287 	if (sr && sr->fid_ofile) {
1288 		sa_mask = set_attr->sa_mask;
1289 		access = 0;
1290 
1291 		if (sa_mask & SMB_AT_SIZE) {
1292 			access |= FILE_WRITE_DATA;
1293 			sa_mask &= ~SMB_AT_SIZE;
1294 		}
1295 
1296 		if (sa_mask & (SMB_AT_UID|SMB_AT_GID)) {
1297 			access |= WRITE_OWNER;
1298 			sa_mask &= ~(SMB_AT_UID|SMB_AT_GID);
1299 		}
1300 
1301 		if (sa_mask)
1302 			access |= FILE_WRITE_ATTRIBUTES;
1303 
1304 		status = smb_ofile_access(sr->fid_ofile, cr, access);
1305 		if (status != NT_STATUS_SUCCESS)
1306 			return (EACCES);
1307 
1308 		if (smb_tree_has_feature(sr->tid_tree,
1309 		    SMB_TREE_ACEMASKONACCESS))
1310 			flags = ATTR_NOACLCHECK;
1311 	}
1312 
1313 	unnamed_node = SMB_IS_STREAM(snode);
1314 
1315 	if (unnamed_node) {
1316 		ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC);
1317 		ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING);
1318 		unnamed_vp = unnamed_node->vp;
1319 	}
1320 
1321 	rc = smb_vop_setattr(snode->vp, unnamed_vp, set_attr, flags, cr);
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  * If name indicates that the file is a stream file, perform
1652  * stream specific lookup, otherwise call smb_fsop_lookup.
1653  *
1654  * Return an error if the looked-up file is in outside the tree.
1655  * (Required when invoked from open path.)
1656  */
1657 
1658 int
1659 smb_fsop_lookup_name(
1660     smb_request_t *sr,
1661     cred_t	*cr,
1662     int		flags,
1663     smb_node_t	*root_node,
1664     smb_node_t	*dir_snode,
1665     char	*name,
1666     smb_node_t	**ret_snode,
1667     smb_attr_t	*ret_attr)
1668 {
1669 	smb_node_t	*fnode;
1670 	smb_attr_t	file_attr;
1671 	vnode_t		*xattrdirvp;
1672 	vnode_t		*vp;
1673 	char		*od_name;
1674 	char		*fname;
1675 	char		*sname;
1676 	int		rc;
1677 
1678 	ASSERT(cr);
1679 	ASSERT(dir_snode);
1680 	ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC);
1681 	ASSERT(dir_snode->n_state != SMB_NODE_STATE_DESTROYING);
1682 
1683 	/*
1684 	 * The following check is required for streams processing, below
1685 	 */
1686 
1687 	if (SMB_TREE_IS_CASEINSENSITIVE(sr))
1688 		flags |= SMB_IGNORE_CASE;
1689 
1690 	fname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
1691 	sname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
1692 
1693 	if (smb_stream_parse_name(name, fname, sname)) {
1694 		/*
1695 		 * Look up the unnamed stream (i.e. fname).
1696 		 * Unmangle processing will be done on fname
1697 		 * as well as any link target.
1698 		 */
1699 		rc = smb_fsop_lookup(sr, cr, flags, root_node, dir_snode, fname,
1700 		    &fnode, &file_attr, NULL, NULL);
1701 
1702 		if (rc != 0) {
1703 			kmem_free(fname, MAXNAMELEN);
1704 			kmem_free(sname, MAXNAMELEN);
1705 			return (rc);
1706 		}
1707 
1708 		od_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
1709 
1710 		/*
1711 		 * od_name is the on-disk name of the stream, except
1712 		 * without the prepended stream prefix (SMB_STREAM_PREFIX)
1713 		 */
1714 
1715 		/*
1716 		 * XXX
1717 		 * What permissions NTFS requires for stream lookup if any?
1718 		 */
1719 		rc = smb_vop_stream_lookup(fnode->vp, sname, &vp, od_name,
1720 		    &xattrdirvp, flags, root_node->vp, cr);
1721 
1722 		if (rc != 0) {
1723 			smb_node_release(fnode);
1724 			kmem_free(fname, MAXNAMELEN);
1725 			kmem_free(sname, MAXNAMELEN);
1726 			kmem_free(od_name, MAXNAMELEN);
1727 			return (rc);
1728 		}
1729 
1730 		*ret_snode = smb_stream_node_lookup(sr, cr, fnode, xattrdirvp,
1731 		    vp, od_name, ret_attr);
1732 
1733 		kmem_free(od_name, MAXNAMELEN);
1734 		smb_node_release(fnode);
1735 
1736 		if (*ret_snode == NULL) {
1737 			VN_RELE(xattrdirvp);
1738 			VN_RELE(vp);
1739 			kmem_free(fname, MAXNAMELEN);
1740 			kmem_free(sname, MAXNAMELEN);
1741 			return (ENOMEM);
1742 		}
1743 	} else {
1744 		rc = smb_fsop_lookup(sr, cr, flags, root_node, dir_snode, name,
1745 		    ret_snode, ret_attr, NULL, NULL);
1746 	}
1747 
1748 	if (rc == 0) {
1749 		ASSERT(ret_snode);
1750 		if (SMB_TREE_CONTAINS_NODE(sr, *ret_snode) == 0) {
1751 			smb_node_release(*ret_snode);
1752 			*ret_snode = NULL;
1753 			rc = EACCES;
1754 		}
1755 	}
1756 
1757 	kmem_free(fname, MAXNAMELEN);
1758 	kmem_free(sname, MAXNAMELEN);
1759 
1760 	return (rc);
1761 }
1762 
1763 /*
1764  * smb_fsop_lookup
1765  *
1766  * All SMB functions should use this smb_vop_lookup wrapper to ensure that
1767  * the smb_vop_lookup is performed with the appropriate credentials and using
1768  * case insensitive compares. Please document any direct call to smb_vop_lookup
1769  * to explain the reason for avoiding this wrapper.
1770  *
1771  * It is assumed that a reference exists on dir_snode coming into this routine
1772  * (and that it is safe from deallocation).
1773  *
1774  * Same with the root_node.
1775  *
1776  * *ret_snode is returned with a reference upon success.  No reference is
1777  * taken if an error is returned.
1778  *
1779  * Note: The returned ret_snode may be in a child mount.  This is ok for
1780  * readdir and getdents.
1781  *
1782  * ret_shortname and ret_name83 must each point to buffers of at least
1783  * SMB_SHORTNAMELEN bytes.
1784  *
1785  * Other smb_fsop_* routines will call SMB_TREE_CONTAINS_NODE() to prevent
1786  * operations on files not in the parent mount.
1787  */
1788 int
1789 smb_fsop_lookup(
1790     smb_request_t *sr,
1791     cred_t	*cr,
1792     int		flags,
1793     smb_node_t	*root_node,
1794     smb_node_t	*dir_snode,
1795     char	*name,
1796     smb_node_t	**ret_snode,
1797     smb_attr_t	*ret_attr,
1798     char	*ret_shortname,
1799     char	*ret_name83)
1800 {
1801 	smb_node_t *lnk_target_node;
1802 	smb_node_t *lnk_dnode;
1803 	char *longname;
1804 	char *od_name;
1805 	vnode_t *vp;
1806 	int rc;
1807 
1808 	ASSERT(cr);
1809 	ASSERT(dir_snode);
1810 	ASSERT(dir_snode->n_magic == SMB_NODE_MAGIC);
1811 	ASSERT(dir_snode->n_state != SMB_NODE_STATE_DESTROYING);
1812 
1813 	if (name == NULL)
1814 		return (EINVAL);
1815 
1816 	if (SMB_TREE_CONTAINS_NODE(sr, dir_snode) == 0)
1817 		return (EACCES);
1818 
1819 	if (SMB_TREE_IS_CASEINSENSITIVE(sr))
1820 		flags |= SMB_IGNORE_CASE;
1821 
1822 	od_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
1823 
1824 	rc = smb_vop_lookup(dir_snode->vp, name, &vp, od_name, flags,
1825 	    root_node ? root_node->vp : NULL, cr);
1826 
1827 	if (rc != 0) {
1828 		if (smb_maybe_mangled_name(name) == 0) {
1829 			kmem_free(od_name, MAXNAMELEN);
1830 			return (rc);
1831 		}
1832 
1833 		longname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
1834 
1835 		rc = smb_unmangle_name(sr, cr, dir_snode, name, longname,
1836 		    MAXNAMELEN, ret_shortname, ret_name83, 1);
1837 
1838 		if (rc != 0) {
1839 			kmem_free(od_name, MAXNAMELEN);
1840 			kmem_free(longname, MAXNAMELEN);
1841 			return (rc);
1842 		}
1843 
1844 		/*
1845 		 * We passed "1" as the "od" parameter
1846 		 * to smb_unmangle_name(), such that longname
1847 		 * is the real (case-sensitive) on-disk name.
1848 		 * We make sure we do a lookup on this exact
1849 		 * name, as the name was mangled and denotes
1850 		 * a unique file.
1851 		 */
1852 
1853 		if (flags & SMB_IGNORE_CASE)
1854 			flags &= ~SMB_IGNORE_CASE;
1855 
1856 		rc = smb_vop_lookup(dir_snode->vp, longname, &vp, od_name,
1857 		    flags, root_node ? root_node->vp : NULL, cr);
1858 
1859 		kmem_free(longname, MAXNAMELEN);
1860 
1861 		if (rc != 0) {
1862 			kmem_free(od_name, MAXNAMELEN);
1863 			return (rc);
1864 		}
1865 	}
1866 
1867 	if ((flags & SMB_FOLLOW_LINKS) && (vp->v_type == VLNK)) {
1868 
1869 		rc = smb_pathname(sr, od_name, FOLLOW, root_node, dir_snode,
1870 		    &lnk_dnode, &lnk_target_node, cr);
1871 
1872 		if (rc != 0) {
1873 			/*
1874 			 * The link is assumed to be for the last component
1875 			 * of a path.  Hence any ENOTDIR error will be returned
1876 			 * as ENOENT.
1877 			 */
1878 			if (rc == ENOTDIR)
1879 				rc = ENOENT;
1880 
1881 			VN_RELE(vp);
1882 			kmem_free(od_name, MAXNAMELEN);
1883 			return (rc);
1884 		}
1885 
1886 		/*
1887 		 * Release the original VLNK vnode
1888 		 */
1889 
1890 		VN_RELE(vp);
1891 		vp = lnk_target_node->vp;
1892 
1893 		rc = smb_vop_traverse_check(&vp);
1894 
1895 		if (rc != 0) {
1896 			smb_node_release(lnk_dnode);
1897 			smb_node_release(lnk_target_node);
1898 			kmem_free(od_name, MAXNAMELEN);
1899 			return (rc);
1900 		}
1901 
1902 		/*
1903 		 * smb_vop_traverse_check() may have returned a different vnode
1904 		 */
1905 
1906 		if (lnk_target_node->vp == vp) {
1907 			*ret_snode = lnk_target_node;
1908 			*ret_attr = (*ret_snode)->attr;
1909 		} else {
1910 			*ret_snode = smb_node_lookup(sr, NULL, cr, vp,
1911 			    lnk_target_node->od_name, lnk_dnode, NULL,
1912 			    ret_attr);
1913 
1914 			if (*ret_snode == NULL) {
1915 				VN_RELE(vp);
1916 				rc = ENOMEM;
1917 			}
1918 			smb_node_release(lnk_target_node);
1919 		}
1920 
1921 		smb_node_release(lnk_dnode);
1922 
1923 	} else {
1924 
1925 		rc = smb_vop_traverse_check(&vp);
1926 		if (rc) {
1927 			VN_RELE(vp);
1928 			kmem_free(od_name, MAXNAMELEN);
1929 			return (rc);
1930 		}
1931 
1932 		*ret_snode = smb_node_lookup(sr, NULL, cr, vp, od_name,
1933 		    dir_snode, NULL, ret_attr);
1934 
1935 		if (*ret_snode == NULL) {
1936 			VN_RELE(vp);
1937 			rc = ENOMEM;
1938 		}
1939 	}
1940 
1941 	kmem_free(od_name, MAXNAMELEN);
1942 	return (rc);
1943 }
1944 
1945 /*
1946  * smb_fsop_stream_readdir()
1947  *
1948  * ret_snode and ret_attr are optional parameters (i.e. NULL may be passed in)
1949  *
1950  * This routine will return only NTFS streams.  If an NTFS stream is not
1951  * found at the offset specified, the directory will be read until an NTFS
1952  * stream is found or until EOF.
1953  *
1954  * Note: Sanity checks done in caller
1955  * (smb_fsop_readdir(), smb_fsop_remove_streams())
1956  */
1957 
1958 int
1959 smb_fsop_stream_readdir(smb_request_t *sr, cred_t *cr, smb_node_t *fnode,
1960     uint32_t *cookiep, struct fs_stream_info *stream_info,
1961     smb_node_t **ret_snode, smb_attr_t *ret_attr)
1962 {
1963 	smb_node_t *ret_snodep = NULL;
1964 	smb_attr_t tmp_attr;
1965 	vnode_t *xattrdirvp;
1966 	vnode_t *vp;
1967 	int rc = 0;
1968 	int flags = 0;
1969 
1970 	/*
1971 	 * XXX NTFS permission requirements if any?
1972 	 */
1973 	ASSERT(cr);
1974 	ASSERT(fnode);
1975 	ASSERT(fnode->n_magic == SMB_NODE_MAGIC);
1976 	ASSERT(fnode->n_state != SMB_NODE_STATE_DESTROYING);
1977 
1978 	if (SMB_TREE_IS_CASEINSENSITIVE(sr))
1979 		flags = SMB_IGNORE_CASE;
1980 
1981 	rc = smb_vop_stream_readdir(fnode->vp, cookiep, stream_info, &vp,
1982 	    &xattrdirvp, flags, cr);
1983 
1984 	if ((rc != 0) || *cookiep == SMB_EOF)
1985 		return (rc);
1986 
1987 	ret_snodep = smb_stream_node_lookup(sr, cr, fnode, xattrdirvp, vp,
1988 	    stream_info->name, &tmp_attr);
1989 
1990 	if (ret_snodep == NULL) {
1991 		VN_RELE(xattrdirvp);
1992 		VN_RELE(vp);
1993 		return (ENOMEM);
1994 	}
1995 
1996 	stream_info->size = tmp_attr.sa_vattr.va_size;
1997 
1998 	if (ret_attr)
1999 		*ret_attr = tmp_attr;
2000 
2001 	if (ret_snode)
2002 		*ret_snode = ret_snodep;
2003 	else
2004 		smb_node_release(ret_snodep);
2005 
2006 	return (rc);
2007 }
2008 
2009 int /*ARGSUSED*/
2010 smb_fsop_commit(smb_request_t *sr, cred_t *cr, smb_node_t *snode)
2011 {
2012 	ASSERT(cr);
2013 	ASSERT(snode);
2014 	ASSERT(snode->n_magic == SMB_NODE_MAGIC);
2015 	ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING);
2016 
2017 	ASSERT(sr);
2018 	ASSERT(sr->tid_tree);
2019 	if (SMB_TREE_IS_READONLY(sr))
2020 		return (EROFS);
2021 
2022 	return (smb_vop_commit(snode->vp, cr));
2023 }
2024 
2025 /*
2026  * smb_fsop_aclread
2027  *
2028  * Retrieve filesystem ACL. Depends on requested ACLs in
2029  * fs_sd->sd_secinfo, it'll set DACL and SACL pointers in
2030  * fs_sd. Note that requesting a DACL/SACL doesn't mean that
2031  * the corresponding field in fs_sd should be non-NULL upon
2032  * return, since the target ACL might not contain that type of
2033  * entries.
2034  *
2035  * Returned ACL is always in ACE_T (aka ZFS) format.
2036  * If successful the allocated memory for the ACL should be freed
2037  * using smb_fsacl_free() or smb_fssd_term()
2038  */
2039 int
2040 smb_fsop_aclread(smb_request_t *sr, cred_t *cr, smb_node_t *snode,
2041     smb_fssd_t *fs_sd)
2042 {
2043 	int error = 0;
2044 	int flags = 0;
2045 	int access = 0;
2046 	acl_t *acl;
2047 	smb_node_t *unnamed_node;
2048 
2049 	ASSERT(cr);
2050 
2051 	if (sr->fid_ofile) {
2052 		if (fs_sd->sd_secinfo & SMB_DACL_SECINFO)
2053 			access = READ_CONTROL;
2054 
2055 		if (fs_sd->sd_secinfo & SMB_SACL_SECINFO)
2056 			access |= ACCESS_SYSTEM_SECURITY;
2057 
2058 		error = smb_ofile_access(sr->fid_ofile, cr, access);
2059 		if (error != NT_STATUS_SUCCESS) {
2060 			return (EACCES);
2061 		}
2062 	}
2063 
2064 	unnamed_node = SMB_IS_STREAM(snode);
2065 	if (unnamed_node) {
2066 		ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC);
2067 		ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING);
2068 		/*
2069 		 * Streams don't have ACL, any read ACL attempt on a stream
2070 		 * should be performed on the unnamed stream.
2071 		 */
2072 		snode = unnamed_node;
2073 	}
2074 
2075 	if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_ACEMASKONACCESS))
2076 		flags = ATTR_NOACLCHECK;
2077 
2078 	error = smb_vop_acl_read(snode->vp, &acl, flags,
2079 	    sr->tid_tree->t_acltype, cr);
2080 	if (error != 0) {
2081 		return (error);
2082 	}
2083 
2084 	error = acl_translate(acl, _ACL_ACE_ENABLED,
2085 	    (snode->vp->v_type == VDIR), fs_sd->sd_uid, fs_sd->sd_gid);
2086 
2087 	if (error == 0) {
2088 		smb_fsacl_split(acl, &fs_sd->sd_zdacl, &fs_sd->sd_zsacl,
2089 		    fs_sd->sd_secinfo);
2090 	}
2091 
2092 	acl_free(acl);
2093 	return (error);
2094 }
2095 
2096 /*
2097  * smb_fsop_aclwrite
2098  *
2099  * Stores the filesystem ACL provided in fs_sd->sd_acl.
2100  */
2101 int
2102 smb_fsop_aclwrite(smb_request_t *sr, cred_t *cr, smb_node_t *snode,
2103     smb_fssd_t *fs_sd)
2104 {
2105 	int target_flavor;
2106 	int error = 0;
2107 	int flags = 0;
2108 	int access = 0;
2109 	acl_t *acl, *dacl, *sacl;
2110 	smb_node_t *unnamed_node;
2111 
2112 	ASSERT(cr);
2113 
2114 	ASSERT(sr);
2115 	ASSERT(sr->tid_tree);
2116 	if (SMB_TREE_IS_READONLY(sr))
2117 		return (EROFS);
2118 
2119 	if (sr->fid_ofile) {
2120 		if (fs_sd->sd_secinfo & SMB_DACL_SECINFO)
2121 			access = WRITE_DAC;
2122 
2123 		if (fs_sd->sd_secinfo & SMB_SACL_SECINFO)
2124 			access |= ACCESS_SYSTEM_SECURITY;
2125 
2126 		error = smb_ofile_access(sr->fid_ofile, cr, access);
2127 		if (error != NT_STATUS_SUCCESS)
2128 			return (EACCES);
2129 	}
2130 
2131 	switch (sr->tid_tree->t_acltype) {
2132 	case ACLENT_T:
2133 		target_flavor = _ACL_ACLENT_ENABLED;
2134 		break;
2135 
2136 	case ACE_T:
2137 		target_flavor = _ACL_ACE_ENABLED;
2138 		break;
2139 	default:
2140 		return (EINVAL);
2141 	}
2142 
2143 	unnamed_node = SMB_IS_STREAM(snode);
2144 	if (unnamed_node) {
2145 		ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC);
2146 		ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING);
2147 		/*
2148 		 * Streams don't have ACL, any write ACL attempt on a stream
2149 		 * should be performed on the unnamed stream.
2150 		 */
2151 		snode = unnamed_node;
2152 	}
2153 
2154 	dacl = fs_sd->sd_zdacl;
2155 	sacl = fs_sd->sd_zsacl;
2156 
2157 	ASSERT(dacl || sacl);
2158 	if ((dacl == NULL) && (sacl == NULL))
2159 		return (EINVAL);
2160 
2161 	if (dacl && sacl)
2162 		acl = smb_fsacl_merge(dacl, sacl);
2163 	else if (dacl)
2164 		acl = dacl;
2165 	else
2166 		acl = sacl;
2167 
2168 	error = acl_translate(acl, target_flavor, (snode->vp->v_type == VDIR),
2169 	    fs_sd->sd_uid, fs_sd->sd_gid);
2170 	if (error == 0) {
2171 		if (smb_tree_has_feature(sr->tid_tree,
2172 		    SMB_TREE_ACEMASKONACCESS))
2173 			flags = ATTR_NOACLCHECK;
2174 
2175 		error = smb_vop_acl_write(snode->vp, acl, flags, cr);
2176 	}
2177 
2178 	if (dacl && sacl)
2179 		acl_free(acl);
2180 
2181 	return (error);
2182 }
2183 
2184 acl_type_t
2185 smb_fsop_acltype(smb_node_t *snode)
2186 {
2187 	return (smb_vop_acl_type(snode->vp));
2188 }
2189 
2190 /*
2191  * smb_fsop_sdread
2192  *
2193  * Read the requested security descriptor items from filesystem.
2194  * The items are specified in fs_sd->sd_secinfo.
2195  */
2196 int
2197 smb_fsop_sdread(smb_request_t *sr, cred_t *cr, smb_node_t *snode,
2198     smb_fssd_t *fs_sd)
2199 {
2200 	int error = 0;
2201 	int getowner = 0;
2202 	cred_t *ga_cred;
2203 	smb_attr_t attr;
2204 
2205 	ASSERT(cr);
2206 	ASSERT(fs_sd);
2207 
2208 	/*
2209 	 * File's uid/gid is fetched in two cases:
2210 	 *
2211 	 * 1. it's explicitly requested
2212 	 *
2213 	 * 2. target ACL is ACE_T (ZFS ACL). They're needed for
2214 	 *    owner@/group@ entries. In this case kcred should be used
2215 	 *    because uid/gid are fetched on behalf of smb server.
2216 	 */
2217 	if (fs_sd->sd_secinfo & (SMB_OWNER_SECINFO | SMB_GROUP_SECINFO)) {
2218 		getowner = 1;
2219 		ga_cred = cr;
2220 	} else if (sr->tid_tree->t_acltype == ACE_T) {
2221 		getowner = 1;
2222 		ga_cred = kcred;
2223 	}
2224 
2225 	if (getowner) {
2226 		/*
2227 		 * Windows require READ_CONTROL to read owner/group SID since
2228 		 * they're part of Security Descriptor.
2229 		 * ZFS only requires read_attribute. Need to have a explicit
2230 		 * access check here.
2231 		 */
2232 		if (sr->fid_ofile == NULL) {
2233 			error = smb_fsop_access(sr, ga_cred, snode,
2234 			    READ_CONTROL);
2235 			if (error)
2236 				return (error);
2237 		}
2238 
2239 		attr.sa_mask = SMB_AT_UID | SMB_AT_GID;
2240 		error = smb_fsop_getattr(sr, ga_cred, snode, &attr);
2241 		if (error == 0) {
2242 			fs_sd->sd_uid = attr.sa_vattr.va_uid;
2243 			fs_sd->sd_gid = attr.sa_vattr.va_gid;
2244 		} else {
2245 			return (error);
2246 		}
2247 	}
2248 
2249 	if (fs_sd->sd_secinfo & SMB_ACL_SECINFO) {
2250 		error = smb_fsop_aclread(sr, cr, snode, fs_sd);
2251 	}
2252 
2253 	return (error);
2254 }
2255 
2256 /*
2257  * smb_fsop_sdmerge
2258  *
2259  * From SMB point of view DACL and SACL are two separate list
2260  * which can be manipulated independently without one affecting
2261  * the other, but entries for both DACL and SACL will end up
2262  * in the same ACL if target filesystem supports ACE_T ACLs.
2263  *
2264  * So, if either DACL or SACL is present in the client set request
2265  * the entries corresponding to the non-present ACL shouldn't
2266  * be touched in the FS ACL.
2267  *
2268  * fs_sd parameter contains DACL and SACL specified by SMB
2269  * client to be set on a file/directory. The client could
2270  * specify both or one of these ACLs (if none is specified
2271  * we don't get this far). When both DACL and SACL are given
2272  * by client the existing ACL should be overwritten. If only
2273  * one of them is specified the entries corresponding to the other
2274  * ACL should not be touched. For example, if only DACL
2275  * is specified in input fs_sd, the function reads audit entries
2276  * of the existing ACL of the file and point fs_sd->sd_zsdacl
2277  * pointer to the fetched SACL, this way when smb_fsop_sdwrite()
2278  * function is called the passed fs_sd would point to the specified
2279  * DACL by client and fetched SACL from filesystem, so the file
2280  * will end up with correct ACL.
2281  */
2282 static int
2283 smb_fsop_sdmerge(smb_request_t *sr, smb_node_t *snode, smb_fssd_t *fs_sd)
2284 {
2285 	smb_fssd_t cur_sd;
2286 	int error = 0;
2287 
2288 	if (sr->tid_tree->t_acltype != ACE_T)
2289 		/* Don't bother if target FS doesn't support ACE_T */
2290 		return (0);
2291 
2292 	if ((fs_sd->sd_secinfo & SMB_ACL_SECINFO) != SMB_ACL_SECINFO) {
2293 		if (fs_sd->sd_secinfo & SMB_DACL_SECINFO) {
2294 			/*
2295 			 * Don't overwrite existing audit entries
2296 			 */
2297 			smb_fssd_init(&cur_sd, SMB_SACL_SECINFO,
2298 			    fs_sd->sd_flags);
2299 
2300 			error = smb_fsop_sdread(sr, kcred, snode, &cur_sd);
2301 			if (error == 0) {
2302 				ASSERT(fs_sd->sd_zsacl == NULL);
2303 				fs_sd->sd_zsacl = cur_sd.sd_zsacl;
2304 				if (fs_sd->sd_zsacl && fs_sd->sd_zdacl)
2305 					fs_sd->sd_zsacl->acl_flags =
2306 					    fs_sd->sd_zdacl->acl_flags;
2307 			}
2308 		} else {
2309 			/*
2310 			 * Don't overwrite existing access entries
2311 			 */
2312 			smb_fssd_init(&cur_sd, SMB_DACL_SECINFO,
2313 			    fs_sd->sd_flags);
2314 
2315 			error = smb_fsop_sdread(sr, kcred, snode, &cur_sd);
2316 			if (error == 0) {
2317 				ASSERT(fs_sd->sd_zdacl == NULL);
2318 				fs_sd->sd_zdacl = cur_sd.sd_zdacl;
2319 				if (fs_sd->sd_zdacl && fs_sd->sd_zsacl)
2320 					fs_sd->sd_zdacl->acl_flags =
2321 					    fs_sd->sd_zsacl->acl_flags;
2322 			}
2323 		}
2324 
2325 		if (error)
2326 			smb_fssd_term(&cur_sd);
2327 	}
2328 
2329 	return (error);
2330 }
2331 
2332 /*
2333  * smb_fsop_sdwrite
2334  *
2335  * Stores the given uid, gid and acl in filesystem.
2336  * Provided items in fs_sd are specified by fs_sd->sd_secinfo.
2337  *
2338  * A SMB security descriptor could contain owner, primary group,
2339  * DACL and SACL. Setting an SD should be atomic but here it has to
2340  * be done via two separate FS operations: VOP_SETATTR and
2341  * VOP_SETSECATTR. Therefore, this function has to simulate the
2342  * atomicity as well as it can.
2343  *
2344  * Get the current uid, gid before setting the new uid/gid
2345  * so if smb_fsop_aclwrite fails they can be restored. root cred is
2346  * used to get currend uid/gid since this operation is performed on
2347  * behalf of the server not the user.
2348  *
2349  * If setting uid/gid fails with EPERM it means that and invalid
2350  * owner has been specified. Callers should translate this to
2351  * STATUS_INVALID_OWNER which is not the normal mapping for EPERM
2352  * in upper layers, so EPERM is mapped to EBADE.
2353  */
2354 int
2355 smb_fsop_sdwrite(smb_request_t *sr, cred_t *cr, smb_node_t *snode,
2356     smb_fssd_t *fs_sd, int overwrite)
2357 {
2358 	int error = 0;
2359 	int access = 0;
2360 	smb_attr_t set_attr;
2361 	smb_attr_t orig_attr;
2362 
2363 	ASSERT(cr);
2364 	ASSERT(fs_sd);
2365 
2366 	ASSERT(sr);
2367 	ASSERT(sr->tid_tree);
2368 	if (SMB_TREE_IS_READONLY(sr))
2369 		return (EROFS);
2370 
2371 	bzero(&set_attr, sizeof (smb_attr_t));
2372 
2373 	if (fs_sd->sd_secinfo & SMB_OWNER_SECINFO) {
2374 		set_attr.sa_vattr.va_uid = fs_sd->sd_uid;
2375 		set_attr.sa_mask |= SMB_AT_UID;
2376 		access |= WRITE_OWNER;
2377 	}
2378 
2379 	if (fs_sd->sd_secinfo & SMB_GROUP_SECINFO) {
2380 		set_attr.sa_vattr.va_gid = fs_sd->sd_gid;
2381 		set_attr.sa_mask |= SMB_AT_GID;
2382 		access |= WRITE_OWNER;
2383 	}
2384 
2385 	if (fs_sd->sd_secinfo & SMB_DACL_SECINFO)
2386 		access |= WRITE_DAC;
2387 
2388 	if (fs_sd->sd_secinfo & SMB_SACL_SECINFO)
2389 		access |= ACCESS_SYSTEM_SECURITY;
2390 
2391 	if (sr->fid_ofile)
2392 		error = smb_ofile_access(sr->fid_ofile, cr, access);
2393 	else
2394 		error = smb_fsop_access(sr, cr, snode, access);
2395 
2396 	if (error)
2397 		return (EACCES);
2398 
2399 	if (set_attr.sa_mask) {
2400 		orig_attr.sa_mask = SMB_AT_UID | SMB_AT_GID;
2401 		error = smb_fsop_getattr(sr, kcred, snode, &orig_attr);
2402 		if (error == 0) {
2403 			error = smb_fsop_setattr(sr, cr, snode, &set_attr,
2404 			    NULL);
2405 			if (error == EPERM)
2406 				error = EBADE;
2407 		}
2408 
2409 		if (error)
2410 			return (error);
2411 	}
2412 
2413 	if (fs_sd->sd_secinfo & SMB_ACL_SECINFO) {
2414 		if (overwrite == 0) {
2415 			error = smb_fsop_sdmerge(sr, snode, fs_sd);
2416 			if (error)
2417 				return (error);
2418 		}
2419 
2420 		error = smb_fsop_aclwrite(sr, cr, snode, fs_sd);
2421 		if (error) {
2422 			/*
2423 			 * Revert uid/gid changes if required.
2424 			 */
2425 			if (set_attr.sa_mask) {
2426 				orig_attr.sa_mask = set_attr.sa_mask;
2427 				(void) smb_fsop_setattr(sr, kcred, snode,
2428 				    &orig_attr, NULL);
2429 			}
2430 		}
2431 	}
2432 
2433 	return (error);
2434 }
2435 
2436 /*
2437  * smb_fsop_sdinherit
2438  *
2439  * Inherit the security descriptor from the parent container.
2440  * This function is called after FS has created the file/folder
2441  * so if this doesn't do anything it means FS inheritance is
2442  * in place.
2443  *
2444  * Do inheritance for ZFS internally.
2445  *
2446  * If we want to let ZFS does the inheritance the
2447  * following setting should be true:
2448  *
2449  *  - aclinherit = passthrough
2450  *  - aclmode = passthrough
2451  *  - smbd umask = 0777
2452  *
2453  * This will result in right effective permissions but
2454  * ZFS will always add 6 ACEs for owner, owning group
2455  * and others to be POSIX compliant. This is not what
2456  * Windows clients/users expect, so we decided that CIFS
2457  * implements Windows rules and overwrite whatever ZFS
2458  * comes up with. This way we also don't have to care
2459  * about ZFS aclinherit and aclmode settings.
2460  */
2461 static int
2462 smb_fsop_sdinherit(smb_request_t *sr, smb_node_t *dnode, smb_fssd_t *fs_sd)
2463 {
2464 	int is_dir;
2465 	acl_t *dacl = NULL;
2466 	acl_t *sacl = NULL;
2467 	ksid_t *owner_sid;
2468 	int error;
2469 
2470 	ASSERT(fs_sd);
2471 
2472 	if (sr->tid_tree->t_acltype != ACE_T) {
2473 		/*
2474 		 * No forced inheritance for non-ZFS filesystems.
2475 		 */
2476 		fs_sd->sd_secinfo = 0;
2477 		return (0);
2478 	}
2479 
2480 
2481 	/* Fetch parent directory's ACL */
2482 	error = smb_fsop_sdread(sr, kcred, dnode, fs_sd);
2483 	if (error) {
2484 		return (error);
2485 	}
2486 
2487 	is_dir = (fs_sd->sd_flags & SMB_FSSD_FLAGS_DIR);
2488 	owner_sid = crgetsid(sr->user_cr, KSID_OWNER);
2489 	ASSERT(owner_sid);
2490 	dacl = smb_fsacl_inherit(fs_sd->sd_zdacl, is_dir, SMB_DACL_SECINFO,
2491 	    owner_sid->ks_id);
2492 	sacl = smb_fsacl_inherit(fs_sd->sd_zsacl, is_dir, SMB_SACL_SECINFO,
2493 	    (uid_t)-1);
2494 
2495 	if (sacl == NULL)
2496 		fs_sd->sd_secinfo &= ~SMB_SACL_SECINFO;
2497 
2498 	smb_fsacl_free(fs_sd->sd_zdacl);
2499 	smb_fsacl_free(fs_sd->sd_zsacl);
2500 
2501 	fs_sd->sd_zdacl = dacl;
2502 	fs_sd->sd_zsacl = sacl;
2503 
2504 	return (0);
2505 }
2506 
2507 /*
2508  * smb_fsop_eaccess
2509  *
2510  * Returns the effective permission of the given credential for the
2511  * specified object.
2512  *
2513  * This is just a workaround. We need VFS/FS support for this.
2514  */
2515 void
2516 smb_fsop_eaccess(smb_request_t *sr, cred_t *cr, smb_node_t *snode,
2517     uint32_t *eaccess)
2518 {
2519 	int access = 0;
2520 	vnode_t *dir_vp;
2521 	smb_node_t *unnamed_node;
2522 
2523 	ASSERT(cr);
2524 	ASSERT(snode);
2525 	ASSERT(snode->n_magic == SMB_NODE_MAGIC);
2526 	ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING);
2527 
2528 	unnamed_node = SMB_IS_STREAM(snode);
2529 	if (unnamed_node) {
2530 		ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC);
2531 		ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING);
2532 		/*
2533 		 * Streams authorization should be performed against the
2534 		 * unnamed stream.
2535 		 */
2536 		snode = unnamed_node;
2537 	}
2538 
2539 	if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_ACEMASKONACCESS)) {
2540 		dir_vp = (snode->dir_snode) ? snode->dir_snode->vp : NULL;
2541 		smb_vop_eaccess(snode->vp, (int *)eaccess, V_ACE_MASK, dir_vp,
2542 		    cr);
2543 		return;
2544 	}
2545 
2546 	/*
2547 	 * FS doesn't understand 32-bit mask
2548 	 */
2549 	smb_vop_eaccess(snode->vp, &access, 0, NULL, cr);
2550 
2551 	*eaccess = READ_CONTROL | FILE_READ_EA | FILE_READ_ATTRIBUTES;
2552 
2553 	if (access & VREAD)
2554 		*eaccess |= FILE_READ_DATA;
2555 
2556 	if (access & VEXEC)
2557 		*eaccess |= FILE_EXECUTE;
2558 
2559 	if (access & VWRITE)
2560 		*eaccess |= FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES |
2561 		    FILE_WRITE_EA | FILE_APPEND_DATA | FILE_DELETE_CHILD;
2562 }
2563 
2564 /*
2565  * smb_fsop_shrlock
2566  *
2567  * For the current open request, check file sharing rules
2568  * against existing opens.
2569  *
2570  * Returns NT_STATUS_SHARING_VIOLATION if there is any
2571  * sharing conflict.  Returns NT_STATUS_SUCCESS otherwise.
2572  *
2573  * Full system-wide share reservation synchronization is available
2574  * when the nbmand (non-blocking mandatory) mount option is set
2575  * (i.e. nbl_need_crit() is true) and nbmand critical regions are used.
2576  * This provides synchronization with NFS and local processes.  The
2577  * critical regions are entered in VOP_SHRLOCK()/fs_shrlock() (called
2578  * from smb_open_subr()/smb_fsop_shrlock()/smb_vop_shrlock()) as well
2579  * as the CIFS rename and delete paths.
2580  *
2581  * The CIFS server will also enter the nbl critical region in the open,
2582  * rename, and delete paths when nbmand is not set.  There is limited
2583  * coordination with local and VFS share reservations in this case.
2584  * Note that when the nbmand mount option is not set, the VFS layer
2585  * only processes advisory reservations and the delete mode is not checked.
2586  *
2587  * Whether or not the nbmand mount option is set, intra-CIFS share
2588  * checking is done in the open, delete, and rename paths using a CIFS
2589  * critical region (node->n_share_lock).
2590  */
2591 
2592 uint32_t
2593 smb_fsop_shrlock(cred_t *cr, smb_node_t *node, uint32_t uniq_fid,
2594     uint32_t desired_access, uint32_t share_access)
2595 {
2596 	int rc;
2597 
2598 	if (node->attr.sa_vattr.va_type == VDIR)
2599 		return (NT_STATUS_SUCCESS);
2600 
2601 	/* Allow access if the request is just for meta data */
2602 	if ((desired_access & FILE_DATA_ALL) == 0)
2603 		return (NT_STATUS_SUCCESS);
2604 
2605 	rc = smb_node_open_check(node, cr, desired_access, share_access);
2606 	if (rc)
2607 		return (NT_STATUS_SHARING_VIOLATION);
2608 
2609 	rc = smb_vop_shrlock(node->vp, uniq_fid, desired_access, share_access,
2610 	    cr);
2611 	if (rc)
2612 		return (NT_STATUS_SHARING_VIOLATION);
2613 
2614 	return (NT_STATUS_SUCCESS);
2615 }
2616 
2617 void
2618 smb_fsop_unshrlock(cred_t *cr, smb_node_t *node, uint32_t uniq_fid)
2619 {
2620 	if (node->attr.sa_vattr.va_type == VDIR)
2621 		return;
2622 
2623 	(void) smb_vop_unshrlock(node->vp, uniq_fid, cr);
2624 }
2625 
2626 int
2627 smb_fsop_frlock(smb_node_t *node, smb_lock_t *lock, boolean_t unlock,
2628     cred_t *cr)
2629 {
2630 	flock64_t bf;
2631 	int flag = F_REMOTELOCK;
2632 
2633 	/*
2634 	 * VOP_FRLOCK() will not be called if:
2635 	 *
2636 	 * 1) The lock has a range of zero bytes. The semantics of Windows and
2637 	 *    POSIX are different. In the case of POSIX it asks for the locking
2638 	 *    of all the bytes from the offset provided until the end of the
2639 	 *    file. In the case of Windows a range of zero locks nothing and
2640 	 *    doesn't conflict with any other lock.
2641 	 *
2642 	 * 2) The lock rolls over (start + lenght < start). Solaris will assert
2643 	 *    if such a request is submitted. This will not create
2644 	 *    incompatibilities between POSIX and Windows. In the Windows world,
2645 	 *    if a client submits such a lock, the server will not lock any
2646 	 *    bytes. Interestingly if the same lock (same offset and length) is
2647 	 *    resubmitted Windows will consider that there is an overlap and
2648 	 *    the granting rules will then apply.
2649 	 */
2650 	if ((lock->l_length == 0) ||
2651 	    ((lock->l_start + lock->l_length - 1) < lock->l_start))
2652 		return (0);
2653 
2654 	bzero(&bf, sizeof (bf));
2655 
2656 	if (unlock) {
2657 		bf.l_type = F_UNLCK;
2658 	} else if (lock->l_type == SMB_LOCK_TYPE_READONLY) {
2659 		bf.l_type = F_RDLCK;
2660 		flag |= FREAD;
2661 	} else if (lock->l_type == SMB_LOCK_TYPE_READWRITE) {
2662 		bf.l_type = F_WRLCK;
2663 		flag |= FWRITE;
2664 	}
2665 
2666 	bf.l_start = lock->l_start;
2667 	bf.l_len = lock->l_length;
2668 	bf.l_pid = lock->l_file->f_uniqid;
2669 	bf.l_sysid = smb_ct.cc_sysid;
2670 
2671 	return (smb_vop_frlock(node->vp, cr, flag, &bf));
2672 }
2673