smb_fsops.c revision 10122:96eda55bfd54
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
38extern caller_context_t smb_ct;
39
40extern int smb_fem_oplock_install(smb_node_t *);
41extern void smb_fem_oplock_uninstall(smb_node_t *);
42
43extern int smb_vop_other_opens(vnode_t *, int);
44
45static int smb_fsop_create_stream(smb_request_t *, cred_t *, smb_node_t *,
46    char *, char *, int, smb_attr_t *, smb_node_t **);
47
48static int smb_fsop_create_file(smb_request_t *, cred_t *, smb_node_t *,
49    char *, int, smb_attr_t *, smb_node_t **);
50
51static 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
54static 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
91int
92smb_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
110int
111smb_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
121void
122smb_fsop_close(smb_node_t *node, int mode, cred_t *cred)
123{
124	smb_vop_close(node->vp, mode, cred);
125}
126
127int
128smb_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
146void
147smb_fsop_oplock_uninstall(smb_node_t *node)
148{
149	smb_fem_oplock_uninstall(node);
150}
151
152static int
153smb_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 */
309int
310smb_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 */
395static int
396smb_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 */
462static int
463smb_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 */
539int
540smb_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 */
663int
664smb_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->n_dnode->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 */
769int
770smb_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 */
835int
836smb_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 */
899int
900smb_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 */
961int
962smb_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 */
1019int
1020smb_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 */
1122int
1123smb_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 */
1211int
1212smb_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 */
1275int
1276smb_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 */
1348int
1349smb_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 */
1376int
1377smb_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->n_dnode) ? snode->n_dnode->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
1488int
1489smb_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 */
1615int
1616smb_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
1764int /*ARGSUSED*/
1765smb_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 */
1794int
1795smb_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 */
1859int
1860smb_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
1945acl_type_t
1946smb_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 */
1957int
1958smb_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 */
2043static int
2044smb_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 */
2115int
2116smb_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 */
2221static int
2222smb_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 */
2275void
2276smb_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->n_dnode) ? snode->n_dnode->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
2353uint32_t
2354smb_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
2378void
2379smb_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
2387int
2388smb_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