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