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