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