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