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