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