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