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