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