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