1*da6c28aaSamw /*
2*da6c28aaSamw  * CDDL HEADER START
3*da6c28aaSamw  *
4*da6c28aaSamw  * The contents of this file are subject to the terms of the
5*da6c28aaSamw  * Common Development and Distribution License (the "License").
6*da6c28aaSamw  * You may not use this file except in compliance with the License.
7*da6c28aaSamw  *
8*da6c28aaSamw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*da6c28aaSamw  * or http://www.opensolaris.org/os/licensing.
10*da6c28aaSamw  * See the License for the specific language governing permissions
11*da6c28aaSamw  * and limitations under the License.
12*da6c28aaSamw  *
13*da6c28aaSamw  * When distributing Covered Code, include this CDDL HEADER in each
14*da6c28aaSamw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*da6c28aaSamw  * If applicable, add the following below this CDDL HEADER, with the
16*da6c28aaSamw  * fields enclosed by brackets "[]" replaced with your own identifying
17*da6c28aaSamw  * information: Portions Copyright [yyyy] [name of copyright owner]
18*da6c28aaSamw  *
19*da6c28aaSamw  * CDDL HEADER END
20*da6c28aaSamw  */
21*da6c28aaSamw /*
22*da6c28aaSamw  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23*da6c28aaSamw  * Use is subject to license terms.
24*da6c28aaSamw  */
25*da6c28aaSamw 
26*da6c28aaSamw #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*da6c28aaSamw 
28*da6c28aaSamw #include <sys/types.h>
29*da6c28aaSamw #include <sys/stat.h>
30*da6c28aaSamw #include <sys/uio.h>
31*da6c28aaSamw #include <sys/statvfs.h>
32*da6c28aaSamw #include <sys/vnode.h>
33*da6c28aaSamw #include <sys/thread.h>
34*da6c28aaSamw #include <sys/pathname.h>
35*da6c28aaSamw #include <sys/cred.h>
36*da6c28aaSamw #include <sys/extdirent.h>
37*da6c28aaSamw #include <acl/acl_common.h>
38*da6c28aaSamw #include <smbsrv/smb_vops.h>
39*da6c28aaSamw #include <smbsrv/string.h>
40*da6c28aaSamw #include <smbsrv/lmshare.h>
41*da6c28aaSamw #include <smbsrv/smbtrans.h>
42*da6c28aaSamw #include <smbsrv/smb_incl.h>
43*da6c28aaSamw #include <smbsrv/smb_fsops.h>
44*da6c28aaSamw 
45*da6c28aaSamw static int
46*da6c28aaSamw smb_vop_readdir_readpage(vnode_t *vp, void *buf, uint32_t offset, int *count,
47*da6c28aaSamw     cred_t *cr, caller_context_t *ct, int flags);
48*da6c28aaSamw 
49*da6c28aaSamw static int
50*da6c28aaSamw smb_vop_readdir_entry(vnode_t *dvp, uint32_t *cookiep, char *name, int *namelen,
51*da6c28aaSamw     ino64_t *inop, vnode_t **vpp, char *od_name, int flags, cred_t *cr,
52*da6c28aaSamw     caller_context_t *ct, char *dirbuf, int num_bytes);
53*da6c28aaSamw 
54*da6c28aaSamw static int
55*da6c28aaSamw smb_vop_getdents_entries(smb_node_t *dir_snode, uint32_t *cookiep,
56*da6c28aaSamw     int32_t *dircountp, char *arg, uint32_t flags, struct smb_request *sr,
57*da6c28aaSamw     cred_t *cr, caller_context_t *ct, char *dirbuf, int *maxentries,
58*da6c28aaSamw     int num_bytes, char *);
59*da6c28aaSamw 
60*da6c28aaSamw extern int
61*da6c28aaSamw smb_gather_dents_info(char *args, ino_t fileid, int namelen,
62*da6c28aaSamw     char *name, uint32_t cookie, int32_t *countp,
63*da6c28aaSamw     smb_attr_t *attr, struct smb_node *snode,
64*da6c28aaSamw     char *shortname, char *name83);
65*da6c28aaSamw 
66*da6c28aaSamw static void
67*da6c28aaSamw smb_sa_to_va_mask(uint_t sa_mask, uint_t *va_maskp);
68*da6c28aaSamw 
69*da6c28aaSamw #define	SMB_AT_MAX	16
70*da6c28aaSamw static uint_t smb_attrmap[SMB_AT_MAX] = {
71*da6c28aaSamw 	0,
72*da6c28aaSamw 	AT_TYPE,
73*da6c28aaSamw 	AT_MODE,
74*da6c28aaSamw 	AT_UID,
75*da6c28aaSamw 	AT_GID,
76*da6c28aaSamw 	AT_FSID,
77*da6c28aaSamw 	AT_NODEID,
78*da6c28aaSamw 	AT_NLINK,
79*da6c28aaSamw 	AT_SIZE,
80*da6c28aaSamw 	AT_ATIME,
81*da6c28aaSamw 	AT_MTIME,
82*da6c28aaSamw 	AT_CTIME,
83*da6c28aaSamw 	AT_RDEV,
84*da6c28aaSamw 	AT_BLKSIZE,
85*da6c28aaSamw 	AT_NBLOCKS,
86*da6c28aaSamw 	AT_SEQ
87*da6c28aaSamw };
88*da6c28aaSamw 
89*da6c28aaSamw int
90*da6c28aaSamw smb_vop_open(vnode_t **vpp, int mode, cred_t *cred, caller_context_t *ct)
91*da6c28aaSamw {
92*da6c28aaSamw 	return (VOP_OPEN(vpp, mode, cred, ct));
93*da6c28aaSamw }
94*da6c28aaSamw 
95*da6c28aaSamw int
96*da6c28aaSamw smb_vop_close(vnode_t *vp, int mode, cred_t *cred, caller_context_t *ct)
97*da6c28aaSamw {
98*da6c28aaSamw 	return (VOP_CLOSE(vp, mode, 1, (offset_t)0, cred, ct));
99*da6c28aaSamw }
100*da6c28aaSamw 
101*da6c28aaSamw /*
102*da6c28aaSamw  * The smb_vop_* functions have minimal knowledge of CIFS semantics and
103*da6c28aaSamw  * serve as an interface to the VFS layer.
104*da6c28aaSamw  *
105*da6c28aaSamw  * Only smb_fsop_* layer functions should call smb_vop_* layer functions.
106*da6c28aaSamw  * (Higher-level CIFS service code should never skip the smb_fsop_* layer
107*da6c28aaSamw  * to call smb_vop_* layer functions directly.)
108*da6c28aaSamw  */
109*da6c28aaSamw 
110*da6c28aaSamw /*
111*da6c28aaSamw  * XXX - Extended attributes support in the file system assumed.
112*da6c28aaSamw  * This is needed for full NT Streams functionality.
113*da6c28aaSamw  */
114*da6c28aaSamw 
115*da6c28aaSamw int
116*da6c28aaSamw smb_vop_read(vnode_t *vp, uio_t *uiop, cred_t *cr, caller_context_t *ct)
117*da6c28aaSamw {
118*da6c28aaSamw 	int error;
119*da6c28aaSamw 
120*da6c28aaSamw 	(void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
121*da6c28aaSamw 	error = VOP_READ(vp, uiop, 0, cr, ct);
122*da6c28aaSamw 	VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
123*da6c28aaSamw 	return (error);
124*da6c28aaSamw }
125*da6c28aaSamw 
126*da6c28aaSamw int
127*da6c28aaSamw smb_vop_write(vnode_t *vp, uio_t *uiop, uint32_t *flag, uint32_t *lcount,
128*da6c28aaSamw     cred_t *cr, caller_context_t *ct)
129*da6c28aaSamw {
130*da6c28aaSamw 	int error;
131*da6c28aaSamw 	int ioflag = 0;
132*da6c28aaSamw 
133*da6c28aaSamw 	*lcount = uiop->uio_resid;
134*da6c28aaSamw 
135*da6c28aaSamw 	if (*flag == FSSTAB_FILE_SYNC)
136*da6c28aaSamw 		ioflag = FSYNC;
137*da6c28aaSamw 
138*da6c28aaSamw 	uiop->uio_llimit = MAXOFFSET_T;
139*da6c28aaSamw 
140*da6c28aaSamw 	(void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL);
141*da6c28aaSamw 	error = VOP_WRITE(vp, uiop, ioflag, cr, ct);
142*da6c28aaSamw 	VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
143*da6c28aaSamw 
144*da6c28aaSamw 	*lcount -= uiop->uio_resid;
145*da6c28aaSamw 
146*da6c28aaSamw 	return (error);
147*da6c28aaSamw }
148*da6c28aaSamw 
149*da6c28aaSamw /*
150*da6c28aaSamw  * smb_vop_getattr()
151*da6c28aaSamw  *
152*da6c28aaSamw  * smb_fsop_getattr()/smb_vop_getattr() should always be called from the CIFS
153*da6c28aaSamw  * service (instead of calling VOP_GETATTR directly) to retrieve attributes
154*da6c28aaSamw  * due to special processing needed for streams files.
155*da6c28aaSamw  *
156*da6c28aaSamw  * All attributes are retrieved.
157*da6c28aaSamw  *
158*da6c28aaSamw  * A named stream's attributes (as far as CIFS is concerned) are those of the
159*da6c28aaSamw  * unnamed (i.e. data) stream (minus the size attribute), and the size of the
160*da6c28aaSamw  * named stream.  Though the file system may store attributes other than size
161*da6c28aaSamw  * with the named stream, these should not be used by CIFS for any purpose.
162*da6c28aaSamw  *
163*da6c28aaSamw  * When vp denotes a named stream, then unnamed_vp should be passed in (denoting
164*da6c28aaSamw  * the corresponding unnamed stream).
165*da6c28aaSamw  */
166*da6c28aaSamw 
167*da6c28aaSamw int
168*da6c28aaSamw smb_vop_getattr(vnode_t *vp, vnode_t *unnamed_vp, smb_attr_t *ret_attr,
169*da6c28aaSamw     int flags, cred_t *cr, caller_context_t *ct)
170*da6c28aaSamw {
171*da6c28aaSamw 	int error;
172*da6c28aaSamw 	vnode_t *use_vp;
173*da6c28aaSamw 	smb_attr_t tmp_attr;
174*da6c28aaSamw 	xvattr_t tmp_xvattr;
175*da6c28aaSamw 	xoptattr_t *xoap = NULL;
176*da6c28aaSamw 
177*da6c28aaSamw 	if (unnamed_vp)
178*da6c28aaSamw 		use_vp = unnamed_vp;
179*da6c28aaSamw 	else
180*da6c28aaSamw 		use_vp = vp;
181*da6c28aaSamw 
182*da6c28aaSamw 	if (vfs_has_feature(use_vp->v_vfsp, VFSFT_XVATTR)) {
183*da6c28aaSamw 		xva_init(&tmp_xvattr);
184*da6c28aaSamw 		xoap = xva_getxoptattr(&tmp_xvattr);
185*da6c28aaSamw 
186*da6c28aaSamw 		ASSERT(xoap);
187*da6c28aaSamw 
188*da6c28aaSamw 		smb_sa_to_va_mask(ret_attr->sa_mask,
189*da6c28aaSamw 		    &tmp_xvattr.xva_vattr.va_mask);
190*da6c28aaSamw 
191*da6c28aaSamw 		XVA_SET_REQ(&tmp_xvattr, XAT_READONLY);
192*da6c28aaSamw 		XVA_SET_REQ(&tmp_xvattr, XAT_HIDDEN);
193*da6c28aaSamw 		XVA_SET_REQ(&tmp_xvattr, XAT_SYSTEM);
194*da6c28aaSamw 		XVA_SET_REQ(&tmp_xvattr, XAT_ARCHIVE);
195*da6c28aaSamw 		XVA_SET_REQ(&tmp_xvattr, XAT_CREATETIME);
196*da6c28aaSamw 
197*da6c28aaSamw 		if ((error = VOP_GETATTR(use_vp, (vattr_t *)&tmp_xvattr, flags,
198*da6c28aaSamw 		    cr, ct)) != 0)
199*da6c28aaSamw 			return (error);
200*da6c28aaSamw 
201*da6c28aaSamw 		ret_attr->sa_vattr = tmp_xvattr.xva_vattr;
202*da6c28aaSamw 
203*da6c28aaSamw 		/*
204*da6c28aaSamw 		 * Copy special attributes to ret_attr parameter
205*da6c28aaSamw 		 */
206*da6c28aaSamw 
207*da6c28aaSamw 		ret_attr->sa_dosattr = 0;
208*da6c28aaSamw 
209*da6c28aaSamw 		ASSERT(tmp_xvattr.xva_vattr.va_mask & AT_XVATTR);
210*da6c28aaSamw 
211*da6c28aaSamw 		xoap = xva_getxoptattr(&tmp_xvattr);
212*da6c28aaSamw 		ASSERT(xoap);
213*da6c28aaSamw 
214*da6c28aaSamw 		if (XVA_ISSET_RTN(&tmp_xvattr, XAT_READONLY)) {
215*da6c28aaSamw 			if (xoap->xoa_readonly)
216*da6c28aaSamw 				ret_attr->sa_dosattr |= FILE_ATTRIBUTE_READONLY;
217*da6c28aaSamw 		}
218*da6c28aaSamw 
219*da6c28aaSamw 		if (XVA_ISSET_RTN(&tmp_xvattr, XAT_HIDDEN)) {
220*da6c28aaSamw 			if (xoap->xoa_hidden)
221*da6c28aaSamw 				ret_attr->sa_dosattr |= FILE_ATTRIBUTE_HIDDEN;
222*da6c28aaSamw 		}
223*da6c28aaSamw 
224*da6c28aaSamw 		if (XVA_ISSET_RTN(&tmp_xvattr, XAT_SYSTEM)) {
225*da6c28aaSamw 			if (xoap->xoa_system)
226*da6c28aaSamw 				ret_attr->sa_dosattr |= FILE_ATTRIBUTE_SYSTEM;
227*da6c28aaSamw 		}
228*da6c28aaSamw 
229*da6c28aaSamw 		if (XVA_ISSET_RTN(&tmp_xvattr, XAT_ARCHIVE)) {
230*da6c28aaSamw 			if (xoap->xoa_archive)
231*da6c28aaSamw 				ret_attr->sa_dosattr |= FILE_ATTRIBUTE_ARCHIVE;
232*da6c28aaSamw 		}
233*da6c28aaSamw 
234*da6c28aaSamw 		ret_attr->sa_crtime = xoap->xoa_createtime;
235*da6c28aaSamw 
236*da6c28aaSamw 		if (unnamed_vp && (ret_attr->sa_mask & SMB_AT_SIZE)) {
237*da6c28aaSamw 			/*
238*da6c28aaSamw 			 * Retrieve stream size attribute into temporary
239*da6c28aaSamw 			 * structure, in case the underlying file system
240*da6c28aaSamw 			 * returns attributes other than the size (we do not
241*da6c28aaSamw 			 * want to have ret_attr's other fields get
242*da6c28aaSamw 			 * overwritten).
243*da6c28aaSamw 			 *
244*da6c28aaSamw 			 * Note that vp is used here, and not use_vp.
245*da6c28aaSamw 			 * Also, only AT_SIZE is needed.
246*da6c28aaSamw 			 */
247*da6c28aaSamw 
248*da6c28aaSamw 			tmp_xvattr.xva_vattr.va_mask = AT_SIZE;
249*da6c28aaSamw 
250*da6c28aaSamw 			if ((error = VOP_GETATTR(vp, (vattr_t *)&tmp_xvattr,
251*da6c28aaSamw 			    flags, cr, ct)) != 0)
252*da6c28aaSamw 				return (error);
253*da6c28aaSamw 
254*da6c28aaSamw 			ret_attr->sa_vattr.va_size =
255*da6c28aaSamw 			    tmp_xvattr.xva_vattr.va_size;
256*da6c28aaSamw 
257*da6c28aaSamw 		}
258*da6c28aaSamw 
259*da6c28aaSamw 		if (ret_attr->sa_vattr.va_type == VDIR) {
260*da6c28aaSamw 			ret_attr->sa_dosattr |= FILE_ATTRIBUTE_DIRECTORY;
261*da6c28aaSamw 		}
262*da6c28aaSamw 
263*da6c28aaSamw 		return (error);
264*da6c28aaSamw 	}
265*da6c28aaSamw 
266*da6c28aaSamw 	/*
267*da6c28aaSamw 	 * Support for file systems without VFSFT_XVATTR
268*da6c28aaSamw 	 */
269*da6c28aaSamw 
270*da6c28aaSamw 	smb_sa_to_va_mask(ret_attr->sa_mask,
271*da6c28aaSamw 	    &ret_attr->sa_vattr.va_mask);
272*da6c28aaSamw 
273*da6c28aaSamw 	error = VOP_GETATTR(use_vp, &ret_attr->sa_vattr, flags, cr, ct);
274*da6c28aaSamw 
275*da6c28aaSamw 	if (error != 0)
276*da6c28aaSamw 		return (error);
277*da6c28aaSamw 
278*da6c28aaSamw 	/*
279*da6c28aaSamw 	 * "Fake" DOS attributes and create time, filesystem doesn't support
280*da6c28aaSamw 	 * them.
281*da6c28aaSamw 	 */
282*da6c28aaSamw 
283*da6c28aaSamw 	ret_attr->sa_dosattr = 0;
284*da6c28aaSamw 	ret_attr->sa_crtime = ret_attr->sa_vattr.va_ctime;
285*da6c28aaSamw 
286*da6c28aaSamw 	if (unnamed_vp && (ret_attr->sa_mask & SMB_AT_SIZE)) {
287*da6c28aaSamw 		/*
288*da6c28aaSamw 		 * Retrieve stream size attribute into temporary structure,
289*da6c28aaSamw 		 * in case the underlying file system returns attributes
290*da6c28aaSamw 		 * other than the size (we do not want to have ret_attr's
291*da6c28aaSamw 		 * other fields get overwritten).
292*da6c28aaSamw 		 *
293*da6c28aaSamw 		 * Note that vp is used here, and not use_vp.
294*da6c28aaSamw 		 * Also, only AT_SIZE is needed.
295*da6c28aaSamw 		 */
296*da6c28aaSamw 
297*da6c28aaSamw 		tmp_attr.sa_vattr.va_mask = AT_SIZE;
298*da6c28aaSamw 		error = VOP_GETATTR(vp, &tmp_attr.sa_vattr, flags, cr, ct);
299*da6c28aaSamw 
300*da6c28aaSamw 		if (error != 0)
301*da6c28aaSamw 			return (error);
302*da6c28aaSamw 
303*da6c28aaSamw 
304*da6c28aaSamw 		ret_attr->sa_vattr.va_size = tmp_attr.sa_vattr.va_size;
305*da6c28aaSamw 	}
306*da6c28aaSamw 
307*da6c28aaSamw 	if (ret_attr->sa_vattr.va_type == VDIR) {
308*da6c28aaSamw 		ret_attr->sa_dosattr |= FILE_ATTRIBUTE_DIRECTORY;
309*da6c28aaSamw 	}
310*da6c28aaSamw 
311*da6c28aaSamw 	return (error);
312*da6c28aaSamw }
313*da6c28aaSamw 
314*da6c28aaSamw /*
315*da6c28aaSamw  * smb_vop_setattr()
316*da6c28aaSamw  *
317*da6c28aaSamw  * smb_fsop_setattr()/smb_vop_setattr() should always be called from the CIFS
318*da6c28aaSamw  * service to set attributes due to special processing for streams files.
319*da6c28aaSamw  *
320*da6c28aaSamw  * When smb_vop_setattr() is called on a named stream file, all indicated
321*da6c28aaSamw  * attributes except the size are set on the unnamed stream file.  The size
322*da6c28aaSamw  * (if indicated) is set on the named stream file.
323*da6c28aaSamw  */
324*da6c28aaSamw 
325*da6c28aaSamw int
326*da6c28aaSamw smb_vop_setattr(vnode_t *vp, vnode_t *unnamed_vp, smb_attr_t *set_attr,
327*da6c28aaSamw     int flags, cred_t *cr, caller_context_t *ct)
328*da6c28aaSamw {
329*da6c28aaSamw 	int error = 0;
330*da6c28aaSamw 	int at_size = 0;
331*da6c28aaSamw 	vnode_t *use_vp;
332*da6c28aaSamw 	xvattr_t tmp_xvattr;
333*da6c28aaSamw 	xoptattr_t *xoap = NULL;
334*da6c28aaSamw 	uint_t xva_mask;
335*da6c28aaSamw 
336*da6c28aaSamw 	if (unnamed_vp) {
337*da6c28aaSamw 		use_vp = unnamed_vp;
338*da6c28aaSamw 		if (set_attr->sa_mask & SMB_AT_SIZE) {
339*da6c28aaSamw 			at_size = 1;
340*da6c28aaSamw 			set_attr->sa_mask &= ~SMB_AT_SIZE;
341*da6c28aaSamw 		}
342*da6c28aaSamw 	} else {
343*da6c28aaSamw 		use_vp = vp;
344*da6c28aaSamw 	}
345*da6c28aaSamw 
346*da6c28aaSamw 	/*
347*da6c28aaSamw 	 * The caller should not be setting sa_vattr.va_mask,
348*da6c28aaSamw 	 * but rather sa_mask.
349*da6c28aaSamw 	 */
350*da6c28aaSamw 
351*da6c28aaSamw 	set_attr->sa_vattr.va_mask = 0;
352*da6c28aaSamw 
353*da6c28aaSamw 	if (vfs_has_feature(use_vp->v_vfsp, VFSFT_XVATTR)) {
354*da6c28aaSamw 		/*
355*da6c28aaSamw 		 * Initialize xvattr, including bzero
356*da6c28aaSamw 		 */
357*da6c28aaSamw 		xva_init(&tmp_xvattr);
358*da6c28aaSamw 		xoap = xva_getxoptattr(&tmp_xvattr);
359*da6c28aaSamw 
360*da6c28aaSamw 		ASSERT(xoap);
361*da6c28aaSamw 
362*da6c28aaSamw 		/*
363*da6c28aaSamw 		 * Copy caller-specified classic attributes to tmp_xvattr.
364*da6c28aaSamw 		 * First save tmp_xvattr's mask (set in xva_init()).
365*da6c28aaSamw 		 * This is |'d in later.
366*da6c28aaSamw 		 */
367*da6c28aaSamw 
368*da6c28aaSamw 		xva_mask = tmp_xvattr.xva_vattr.va_mask;
369*da6c28aaSamw 		tmp_xvattr.xva_vattr = set_attr->sa_vattr;
370*da6c28aaSamw 
371*da6c28aaSamw 		smb_sa_to_va_mask(set_attr->sa_mask,
372*da6c28aaSamw 		    &tmp_xvattr.xva_vattr.va_mask);
373*da6c28aaSamw 
374*da6c28aaSamw 		/*
375*da6c28aaSamw 		 * "|" in the original xva_mask.
376*da6c28aaSamw 		 */
377*da6c28aaSamw 
378*da6c28aaSamw 		tmp_xvattr.xva_vattr.va_mask |= xva_mask;
379*da6c28aaSamw 
380*da6c28aaSamw 		if (set_attr->sa_mask & SMB_AT_DOSATTR) {
381*da6c28aaSamw 			XVA_SET_REQ(&tmp_xvattr, XAT_ARCHIVE);
382*da6c28aaSamw 			XVA_SET_REQ(&tmp_xvattr, XAT_SYSTEM);
383*da6c28aaSamw 			XVA_SET_REQ(&tmp_xvattr, XAT_READONLY);
384*da6c28aaSamw 			XVA_SET_REQ(&tmp_xvattr, XAT_HIDDEN);
385*da6c28aaSamw 
386*da6c28aaSamw 			/*
387*da6c28aaSamw 			 * set_attr->sa_dosattr: If a given bit is not set,
388*da6c28aaSamw 			 * that indicates that the corresponding field needs
389*da6c28aaSamw 			 * to be updated with a "0" value.  This is done
390*da6c28aaSamw 			 * implicitly as the xoap->xoa_* fields were bzero'd.
391*da6c28aaSamw 			 */
392*da6c28aaSamw 
393*da6c28aaSamw 			if (set_attr->sa_dosattr & FILE_ATTRIBUTE_ARCHIVE)
394*da6c28aaSamw 				xoap->xoa_archive = 1;
395*da6c28aaSamw 
396*da6c28aaSamw 			if (set_attr->sa_dosattr & FILE_ATTRIBUTE_SYSTEM)
397*da6c28aaSamw 				xoap->xoa_system = 1;
398*da6c28aaSamw 
399*da6c28aaSamw 			if (set_attr->sa_dosattr & FILE_ATTRIBUTE_READONLY)
400*da6c28aaSamw 				xoap->xoa_readonly = 1;
401*da6c28aaSamw 
402*da6c28aaSamw 			if (set_attr->sa_dosattr & FILE_ATTRIBUTE_HIDDEN)
403*da6c28aaSamw 				xoap->xoa_hidden = 1;
404*da6c28aaSamw 		}
405*da6c28aaSamw 
406*da6c28aaSamw 		if (set_attr->sa_mask & SMB_AT_CRTIME) {
407*da6c28aaSamw 			XVA_SET_REQ(&tmp_xvattr, XAT_CREATETIME);
408*da6c28aaSamw 			xoap->xoa_createtime = set_attr->sa_crtime;
409*da6c28aaSamw 		}
410*da6c28aaSamw 
411*da6c28aaSamw 		if ((error = VOP_SETATTR(use_vp, (vattr_t *)&tmp_xvattr, flags,
412*da6c28aaSamw 		    cr, ct)) != 0)
413*da6c28aaSamw 			return (error);
414*da6c28aaSamw 
415*da6c28aaSamw 		/*
416*da6c28aaSamw 		 * If the size of the stream needs to be set, set it on
417*da6c28aaSamw 		 * the stream file directly.  (All other indicated attributes
418*da6c28aaSamw 		 * are set on the stream's unnamed stream, above.)
419*da6c28aaSamw 		 */
420*da6c28aaSamw 
421*da6c28aaSamw 		if (at_size) {
422*da6c28aaSamw 			/*
423*da6c28aaSamw 			 * set_attr->sa_vattr.va_size already contains the
424*da6c28aaSamw 			 * size as set by the caller
425*da6c28aaSamw 			 *
426*da6c28aaSamw 			 * Note that vp is used here, and not use_vp.
427*da6c28aaSamw 			 * Also, only AT_SIZE is needed.
428*da6c28aaSamw 			 */
429*da6c28aaSamw 
430*da6c28aaSamw 			set_attr->sa_vattr.va_mask = AT_SIZE;
431*da6c28aaSamw 			error = VOP_SETATTR(vp, &set_attr->sa_vattr, flags,
432*da6c28aaSamw 			    cr, ct);
433*da6c28aaSamw 		}
434*da6c28aaSamw 
435*da6c28aaSamw 		return (error);
436*da6c28aaSamw 	}
437*da6c28aaSamw 
438*da6c28aaSamw 	/*
439*da6c28aaSamw 	 * Support for file systems without VFSFT_XVATTR
440*da6c28aaSamw 	 */
441*da6c28aaSamw 
442*da6c28aaSamw 	smb_sa_to_va_mask(set_attr->sa_mask, &set_attr->sa_vattr.va_mask);
443*da6c28aaSamw 
444*da6c28aaSamw 	/*
445*da6c28aaSamw 	 * set_attr->sa_vattr already contains new values
446*da6c28aaSamw 	 * as set by the caller
447*da6c28aaSamw 	 */
448*da6c28aaSamw 
449*da6c28aaSamw 	error = VOP_SETATTR(use_vp, &set_attr->sa_vattr, flags, cr, ct);
450*da6c28aaSamw 
451*da6c28aaSamw 	if (error != 0)
452*da6c28aaSamw 		return (error);
453*da6c28aaSamw 
454*da6c28aaSamw 	if (at_size) {
455*da6c28aaSamw 		/*
456*da6c28aaSamw 		 * set_attr->sa_vattr.va_size already contains the
457*da6c28aaSamw 		 * size as set by the caller
458*da6c28aaSamw 		 *
459*da6c28aaSamw 		 * Note that vp is used here, and not use_vp.
460*da6c28aaSamw 		 * Also, only AT_SIZE is needed.
461*da6c28aaSamw 		 */
462*da6c28aaSamw 
463*da6c28aaSamw 		set_attr->sa_vattr.va_mask = AT_SIZE;
464*da6c28aaSamw 		error = VOP_SETATTR(vp, &set_attr->sa_vattr, flags, cr, ct);
465*da6c28aaSamw 	}
466*da6c28aaSamw 
467*da6c28aaSamw 	return (error);
468*da6c28aaSamw }
469*da6c28aaSamw 
470*da6c28aaSamw /*
471*da6c28aaSamw  * smb_vop_access
472*da6c28aaSamw  *
473*da6c28aaSamw  * This is a wrapper round VOP_ACCESS. VOP_ACCESS checks the given mode
474*da6c28aaSamw  * against file's ACL or Unix permissions. CIFS on the other hand needs to
475*da6c28aaSamw  * know if the requested operation can succeed for the given object, this
476*da6c28aaSamw  * requires more checks in case of DELETE bit since permissions on the parent
477*da6c28aaSamw  * directory are important as well. Based on Windows rules if parent's ACL
478*da6c28aaSamw  * grant FILE_DELETE_CHILD a file can be delete regardless of the file's
479*da6c28aaSamw  * permissions.
480*da6c28aaSamw  */
481*da6c28aaSamw int
482*da6c28aaSamw smb_vop_access(vnode_t *vp, int mode, int flags, vnode_t *dir_vp, cred_t *cr)
483*da6c28aaSamw {
484*da6c28aaSamw 	int error = 0;
485*da6c28aaSamw 
486*da6c28aaSamw 	if (mode == 0)
487*da6c28aaSamw 		return (0);
488*da6c28aaSamw 
489*da6c28aaSamw 	if ((flags == V_ACE_MASK) && (mode & ACE_DELETE)) {
490*da6c28aaSamw 		if (dir_vp) {
491*da6c28aaSamw 			error = VOP_ACCESS(dir_vp, ACE_DELETE_CHILD, flags,
492*da6c28aaSamw 			    cr, NULL);
493*da6c28aaSamw 
494*da6c28aaSamw 			if (error == 0)
495*da6c28aaSamw 				mode &= ~ACE_DELETE;
496*da6c28aaSamw 		}
497*da6c28aaSamw 	}
498*da6c28aaSamw 
499*da6c28aaSamw 	if (mode) {
500*da6c28aaSamw 		error = VOP_ACCESS(vp, mode, flags, cr, NULL);
501*da6c28aaSamw 	}
502*da6c28aaSamw 
503*da6c28aaSamw 	return (error);
504*da6c28aaSamw }
505*da6c28aaSamw 
506*da6c28aaSamw /*
507*da6c28aaSamw  * smb_vop_lookup
508*da6c28aaSamw  *
509*da6c28aaSamw  * dvp:		directory vnode (in)
510*da6c28aaSamw  * name:	name of file to be looked up (in)
511*da6c28aaSamw  * vpp:		looked-up vnode (out)
512*da6c28aaSamw  * od_name:	on-disk name of file (out).
513*da6c28aaSamw  *		This parameter is optional.  If a pointer is passed in, it
514*da6c28aaSamw  * 		must be allocated with MAXNAMELEN bytes
515*da6c28aaSamw  * rootvp:	vnode of the tree root (in)
516*da6c28aaSamw  *		This parameter is always passed in non-NULL except at the time
517*da6c28aaSamw  *		of share set up.
518*da6c28aaSamw  */
519*da6c28aaSamw 
520*da6c28aaSamw int
521*da6c28aaSamw smb_vop_lookup(vnode_t *dvp, char *name, vnode_t **vpp, char *od_name,
522*da6c28aaSamw     int flags, vnode_t *rootvp, cred_t *cr, caller_context_t *ct)
523*da6c28aaSamw {
524*da6c28aaSamw 	int error = 0;
525*da6c28aaSamw 	int option_flags = 0;
526*da6c28aaSamw 	pathname_t rpn;
527*da6c28aaSamw 
528*da6c28aaSamw 	if (*name == '\0')
529*da6c28aaSamw 		return (EINVAL);
530*da6c28aaSamw 
531*da6c28aaSamw 	ASSERT(vpp);
532*da6c28aaSamw 	*vpp = NULL;
533*da6c28aaSamw 
534*da6c28aaSamw 	if ((name[0] == '.') && (name[1] == '.') && (name[2] == 0)) {
535*da6c28aaSamw 		if (rootvp && (dvp == rootvp)) {
536*da6c28aaSamw 			VN_HOLD(dvp);
537*da6c28aaSamw 			*vpp = dvp;
538*da6c28aaSamw 			return (0);
539*da6c28aaSamw 		}
540*da6c28aaSamw 
541*da6c28aaSamw 		if (dvp->v_flag & VROOT) {
542*da6c28aaSamw 			vfs_t *vfsp;
543*da6c28aaSamw 			vnode_t *cvp = dvp;
544*da6c28aaSamw 
545*da6c28aaSamw 			/*
546*da6c28aaSamw 			 * Set dvp and check for races with forced unmount
547*da6c28aaSamw 			 * (see lookuppnvp())
548*da6c28aaSamw 			 */
549*da6c28aaSamw 
550*da6c28aaSamw 			vfsp = cvp->v_vfsp;
551*da6c28aaSamw 			vfs_rlock_wait(vfsp);
552*da6c28aaSamw 			if (((dvp = cvp->v_vfsp->vfs_vnodecovered) == NULL) ||
553*da6c28aaSamw 			    (cvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)) {
554*da6c28aaSamw 				vfs_unlock(vfsp);
555*da6c28aaSamw 				return (EIO);
556*da6c28aaSamw 			}
557*da6c28aaSamw 			vfs_unlock(vfsp);
558*da6c28aaSamw 		}
559*da6c28aaSamw 	}
560*da6c28aaSamw 
561*da6c28aaSamw 
562*da6c28aaSamw 
563*da6c28aaSamw 	if (flags & SMB_IGNORE_CASE)
564*da6c28aaSamw 		option_flags = FIGNORECASE;
565*da6c28aaSamw 
566*da6c28aaSamw 	pn_alloc(&rpn);
567*da6c28aaSamw 
568*da6c28aaSamw 	error = VOP_LOOKUP(dvp, name, vpp, NULL, option_flags, NULL, cr,
569*da6c28aaSamw 	    ct, NULL, &rpn);
570*da6c28aaSamw 
571*da6c28aaSamw 	if ((error == 0) && od_name) {
572*da6c28aaSamw 		bzero(od_name, MAXNAMELEN);
573*da6c28aaSamw 		if (option_flags == FIGNORECASE)
574*da6c28aaSamw 			(void) strlcpy(od_name, rpn.pn_buf, MAXNAMELEN);
575*da6c28aaSamw 		else
576*da6c28aaSamw 			(void) strlcpy(od_name, name, MAXNAMELEN);
577*da6c28aaSamw 	}
578*da6c28aaSamw 
579*da6c28aaSamw 	pn_free(&rpn);
580*da6c28aaSamw 	return (error);
581*da6c28aaSamw }
582*da6c28aaSamw 
583*da6c28aaSamw int
584*da6c28aaSamw smb_vop_create(vnode_t *dvp, char *name, smb_attr_t *attr, vnode_t **vpp,
585*da6c28aaSamw     int flags, cred_t *cr, caller_context_t *ct, vsecattr_t *vsap)
586*da6c28aaSamw {
587*da6c28aaSamw 	int error;
588*da6c28aaSamw 	int option_flags = 0;
589*da6c28aaSamw 
590*da6c28aaSamw 	if (flags & SMB_IGNORE_CASE)
591*da6c28aaSamw 		option_flags = FIGNORECASE;
592*da6c28aaSamw 
593*da6c28aaSamw 	smb_sa_to_va_mask(attr->sa_mask, &attr->sa_vattr.va_mask);
594*da6c28aaSamw 
595*da6c28aaSamw 	error = VOP_CREATE(dvp, name, &attr->sa_vattr, EXCL,
596*da6c28aaSamw 	    attr->sa_vattr.va_mode, vpp, cr, option_flags, ct, vsap);
597*da6c28aaSamw 
598*da6c28aaSamw 	return (error);
599*da6c28aaSamw }
600*da6c28aaSamw 
601*da6c28aaSamw int
602*da6c28aaSamw smb_vop_remove(vnode_t *dvp, char *name, int flags, cred_t *cr,
603*da6c28aaSamw     caller_context_t *ct)
604*da6c28aaSamw {
605*da6c28aaSamw 	int error;
606*da6c28aaSamw 	int option_flags = 0;
607*da6c28aaSamw 
608*da6c28aaSamw 	if (flags & SMB_IGNORE_CASE)
609*da6c28aaSamw 		option_flags = FIGNORECASE;
610*da6c28aaSamw 
611*da6c28aaSamw 	error = VOP_REMOVE(dvp, name, cr, ct, option_flags);
612*da6c28aaSamw 
613*da6c28aaSamw 	return (error);
614*da6c28aaSamw }
615*da6c28aaSamw 
616*da6c28aaSamw /*
617*da6c28aaSamw  * smb_vop_rename()
618*da6c28aaSamw  *
619*da6c28aaSamw  * The rename is for files in the same tree (identical TID) only.
620*da6c28aaSamw  */
621*da6c28aaSamw 
622*da6c28aaSamw int
623*da6c28aaSamw smb_vop_rename(vnode_t *from_dvp, char *from_name, vnode_t *to_dvp,
624*da6c28aaSamw     char *to_name, int flags, cred_t *cr, caller_context_t *ct)
625*da6c28aaSamw {
626*da6c28aaSamw 	int error;
627*da6c28aaSamw 	int option_flags = 0;
628*da6c28aaSamw 
629*da6c28aaSamw 
630*da6c28aaSamw 	if (flags & SMB_IGNORE_CASE)
631*da6c28aaSamw 		option_flags = FIGNORECASE;
632*da6c28aaSamw 
633*da6c28aaSamw 	error = VOP_RENAME(from_dvp, from_name, to_dvp, to_name, cr,
634*da6c28aaSamw 	    ct, option_flags);
635*da6c28aaSamw 
636*da6c28aaSamw 	return (error);
637*da6c28aaSamw }
638*da6c28aaSamw 
639*da6c28aaSamw int
640*da6c28aaSamw smb_vop_mkdir(vnode_t *dvp, char *name, smb_attr_t *attr, vnode_t **vpp,
641*da6c28aaSamw     int flags, cred_t *cr, caller_context_t *ct, vsecattr_t *vsap)
642*da6c28aaSamw {
643*da6c28aaSamw 	int error;
644*da6c28aaSamw 	int option_flags = 0;
645*da6c28aaSamw 
646*da6c28aaSamw 
647*da6c28aaSamw 
648*da6c28aaSamw 	if (flags & SMB_IGNORE_CASE)
649*da6c28aaSamw 		option_flags = FIGNORECASE;
650*da6c28aaSamw 
651*da6c28aaSamw 	smb_sa_to_va_mask(attr->sa_mask, &attr->sa_vattr.va_mask);
652*da6c28aaSamw 
653*da6c28aaSamw 	error = VOP_MKDIR(dvp, name, &attr->sa_vattr, vpp, cr, ct,
654*da6c28aaSamw 	    option_flags, vsap);
655*da6c28aaSamw 
656*da6c28aaSamw 	return (error);
657*da6c28aaSamw }
658*da6c28aaSamw 
659*da6c28aaSamw /*
660*da6c28aaSamw  * smb_vop_rmdir()
661*da6c28aaSamw  *
662*da6c28aaSamw  * Only simple rmdir supported, consistent with NT semantics
663*da6c28aaSamw  * (can only remove an empty directory).
664*da6c28aaSamw  *
665*da6c28aaSamw  */
666*da6c28aaSamw 
667*da6c28aaSamw int
668*da6c28aaSamw smb_vop_rmdir(vnode_t *dvp, char *name, int flags, cred_t *cr,
669*da6c28aaSamw     caller_context_t *ct)
670*da6c28aaSamw {
671*da6c28aaSamw 	int error;
672*da6c28aaSamw 	int option_flags = 0;
673*da6c28aaSamw 
674*da6c28aaSamw 	if (flags & SMB_IGNORE_CASE)
675*da6c28aaSamw 		option_flags = FIGNORECASE;
676*da6c28aaSamw 
677*da6c28aaSamw 	/*
678*da6c28aaSamw 	 * Comments adapted from rfs_rmdir().
679*da6c28aaSamw 	 *
680*da6c28aaSamw 	 * VOP_RMDIR now takes a new third argument (the current
681*da6c28aaSamw 	 * directory of the process).  That's because rmdir
682*da6c28aaSamw 	 * wants to return EINVAL if one tries to remove ".".
683*da6c28aaSamw 	 * Of course, SMB servers do not know what their
684*da6c28aaSamw 	 * clients' current directories are.  We fake it by
685*da6c28aaSamw 	 * supplying a vnode known to exist and illegal to
686*da6c28aaSamw 	 * remove.
687*da6c28aaSamw 	 */
688*da6c28aaSamw 
689*da6c28aaSamw 	error = VOP_RMDIR(dvp, name, rootdir, cr, ct, option_flags);
690*da6c28aaSamw 	return (error);
691*da6c28aaSamw }
692*da6c28aaSamw 
693*da6c28aaSamw int
694*da6c28aaSamw smb_vop_commit(vnode_t *vp, cred_t *cr, caller_context_t *ct)
695*da6c28aaSamw {
696*da6c28aaSamw 	return (VOP_FSYNC(vp, 1, cr, ct));
697*da6c28aaSamw }
698*da6c28aaSamw 
699*da6c28aaSamw /*
700*da6c28aaSamw  * smb_vop_readdir()
701*da6c28aaSamw  *
702*da6c28aaSamw  * Upon return, the "name" field will contain either the on-disk name or, if
703*da6c28aaSamw  * it needs mangling or has a case-insensitive collision, the mangled
704*da6c28aaSamw  * "shortname."
705*da6c28aaSamw  *
706*da6c28aaSamw  * vpp is an optional parameter.  If non-NULL, it will contain a pointer to
707*da6c28aaSamw  * the vnode for the name that is looked up (the vnode will be returned held).
708*da6c28aaSamw  *
709*da6c28aaSamw  * od_name is an optional parameter (NULL can be passed if the on-disk name
710*da6c28aaSamw  * is not needed by the caller).
711*da6c28aaSamw  */
712*da6c28aaSamw 
713*da6c28aaSamw int
714*da6c28aaSamw smb_vop_readdir(vnode_t *dvp, uint32_t *cookiep, char *name, int *namelen,
715*da6c28aaSamw     ino64_t *inop, vnode_t **vpp, char *od_name, int flags, cred_t *cr,
716*da6c28aaSamw     caller_context_t *ct)
717*da6c28aaSamw {
718*da6c28aaSamw 	int num_bytes;
719*da6c28aaSamw 	int error = 0;
720*da6c28aaSamw 	char *dirbuf = NULL;
721*da6c28aaSamw 
722*da6c28aaSamw 	ASSERT(dvp);
723*da6c28aaSamw 	ASSERT(cookiep);
724*da6c28aaSamw 	ASSERT(name);
725*da6c28aaSamw 	ASSERT(namelen);
726*da6c28aaSamw 	ASSERT(inop);
727*da6c28aaSamw 	ASSERT(cr);
728*da6c28aaSamw 	ASSERT(ct);
729*da6c28aaSamw 
730*da6c28aaSamw 	if (dvp->v_type != VDIR) {
731*da6c28aaSamw 		*namelen = 0;
732*da6c28aaSamw 		return (ENOTDIR);
733*da6c28aaSamw 	}
734*da6c28aaSamw 
735*da6c28aaSamw 	if (vpp)
736*da6c28aaSamw 		*vpp = NULL;
737*da6c28aaSamw 
738*da6c28aaSamw 	dirbuf = kmem_zalloc(SMB_MINLEN_RDDIR_BUF, KM_SLEEP);
739*da6c28aaSamw 	num_bytes = SMB_MINLEN_RDDIR_BUF;
740*da6c28aaSamw 
741*da6c28aaSamw 	/*
742*da6c28aaSamw 	 * The goal is to retrieve the first valid entry from *cookiep
743*da6c28aaSamw 	 * forward.  smb_vop_readdir_readpage() collects an
744*da6c28aaSamw 	 * SMB_MINLEN_RDDIR_BUF-size "page" of directory entry information.
745*da6c28aaSamw 	 * smb_vop_readdir_entry() attempts to find the first valid entry
746*da6c28aaSamw 	 * in that page.
747*da6c28aaSamw 	 */
748*da6c28aaSamw 
749*da6c28aaSamw 	while ((error = smb_vop_readdir_readpage(dvp, dirbuf, *cookiep,
750*da6c28aaSamw 	    &num_bytes, cr, ct, flags)) == 0) {
751*da6c28aaSamw 
752*da6c28aaSamw 		if (num_bytes <= 0)
753*da6c28aaSamw 			break;
754*da6c28aaSamw 
755*da6c28aaSamw 		name[0] = '\0';
756*da6c28aaSamw 
757*da6c28aaSamw 		error = smb_vop_readdir_entry(dvp, cookiep, name, namelen,
758*da6c28aaSamw 		    inop, vpp, od_name, flags, cr, ct, dirbuf,
759*da6c28aaSamw 		    num_bytes);
760*da6c28aaSamw 
761*da6c28aaSamw 		if (error)
762*da6c28aaSamw 			break;
763*da6c28aaSamw 
764*da6c28aaSamw 		if (*name)
765*da6c28aaSamw 			break;
766*da6c28aaSamw 
767*da6c28aaSamw 		bzero(dirbuf, SMB_MINLEN_RDDIR_BUF);
768*da6c28aaSamw 		num_bytes = SMB_MINLEN_RDDIR_BUF;
769*da6c28aaSamw 	}
770*da6c28aaSamw 
771*da6c28aaSamw 
772*da6c28aaSamw 	if (error) {
773*da6c28aaSamw 		kmem_free(dirbuf, SMB_MINLEN_RDDIR_BUF);
774*da6c28aaSamw 		*namelen = 0;
775*da6c28aaSamw 		return (error);
776*da6c28aaSamw 	}
777*da6c28aaSamw 
778*da6c28aaSamw 	if (num_bytes == 0) { /* EOF */
779*da6c28aaSamw 		kmem_free(dirbuf, SMB_MINLEN_RDDIR_BUF);
780*da6c28aaSamw 		*cookiep = SMB_EOF;
781*da6c28aaSamw 		*namelen = 0;
782*da6c28aaSamw 		return (0);
783*da6c28aaSamw 	}
784*da6c28aaSamw 
785*da6c28aaSamw 	kmem_free(dirbuf, SMB_MINLEN_RDDIR_BUF);
786*da6c28aaSamw 	return (0);
787*da6c28aaSamw }
788*da6c28aaSamw 
789*da6c28aaSamw /*
790*da6c28aaSamw  * smb_vop_readdir_readpage()
791*da6c28aaSamw  *
792*da6c28aaSamw  * Collects an SMB_MINLEN_RDDIR_BUF "page" of directory entries.  (The
793*da6c28aaSamw  * directory entries are returned in an fs-independent format by the
794*da6c28aaSamw  * underlying file system.  That is, the "page" of information returned is
795*da6c28aaSamw  * not literally stored on-disk in the format returned.)
796*da6c28aaSamw  *
797*da6c28aaSamw  * Much of the following is borrowed from getdents64()
798*da6c28aaSamw  *
799*da6c28aaSamw  * MAXGETDENTS_SIZE is defined in getdents.c
800*da6c28aaSamw  */
801*da6c28aaSamw 
802*da6c28aaSamw #define	MAXGETDENTS_SIZE	(64 * 1024)
803*da6c28aaSamw 
804*da6c28aaSamw static int
805*da6c28aaSamw smb_vop_readdir_readpage(vnode_t *vp, void *buf, uint32_t offset, int *count,
806*da6c28aaSamw     cred_t *cr, caller_context_t *ct, int flags)
807*da6c28aaSamw {
808*da6c28aaSamw 	int error = 0;
809*da6c28aaSamw 	int rdirent_flags = 0;
810*da6c28aaSamw 	int sink;
811*da6c28aaSamw 	struct uio auio;
812*da6c28aaSamw 	struct iovec aiov;
813*da6c28aaSamw 
814*da6c28aaSamw 	if (vp->v_type != VDIR)
815*da6c28aaSamw 		return (ENOTDIR);
816*da6c28aaSamw 
817*da6c28aaSamw 	/* entflags not working for streams so don't try to use them */
818*da6c28aaSamw 	if (!(flags & SMB_STREAM_RDDIR) &&
819*da6c28aaSamw 	    (vfs_has_feature(vp->v_vfsp, VFSFT_DIRENTFLAGS))) {
820*da6c28aaSamw 		/*
821*da6c28aaSamw 		 * Setting V_RDDIR_ENTFLAGS will cause the buffer to
822*da6c28aaSamw 		 * be filled with edirent_t structures (instead of
823*da6c28aaSamw 		 * dirent64_t structures).
824*da6c28aaSamw 		 */
825*da6c28aaSamw 		rdirent_flags = V_RDDIR_ENTFLAGS;
826*da6c28aaSamw 
827*da6c28aaSamw 		if (*count < sizeof (edirent_t))
828*da6c28aaSamw 			return (EINVAL);
829*da6c28aaSamw 	} else {
830*da6c28aaSamw 		if (*count < sizeof (dirent64_t))
831*da6c28aaSamw 			return (EINVAL);
832*da6c28aaSamw 	}
833*da6c28aaSamw 
834*da6c28aaSamw 	if (*count > MAXGETDENTS_SIZE)
835*da6c28aaSamw 		*count = MAXGETDENTS_SIZE;
836*da6c28aaSamw 
837*da6c28aaSamw 	aiov.iov_base = buf;
838*da6c28aaSamw 	aiov.iov_len = *count;
839*da6c28aaSamw 	auio.uio_iov = &aiov;
840*da6c28aaSamw 	auio.uio_iovcnt = 1;
841*da6c28aaSamw 	auio.uio_loffset = (uint64_t)offset;
842*da6c28aaSamw 	auio.uio_segflg = UIO_SYSSPACE;
843*da6c28aaSamw 	auio.uio_resid = *count;
844*da6c28aaSamw 	auio.uio_fmode = 0;
845*da6c28aaSamw 
846*da6c28aaSamw 	(void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
847*da6c28aaSamw 	error = VOP_READDIR(vp, &auio, cr, &sink, ct, rdirent_flags);
848*da6c28aaSamw 	VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
849*da6c28aaSamw 
850*da6c28aaSamw 	if (error) {
851*da6c28aaSamw 		if (error == ENOENT) {
852*da6c28aaSamw 			/* Fake EOF if offset is bad due to dropping of lock */
853*da6c28aaSamw 			*count = 0;
854*da6c28aaSamw 			return (0);
855*da6c28aaSamw 		} else {
856*da6c28aaSamw 			return (error);
857*da6c28aaSamw 		}
858*da6c28aaSamw 	}
859*da6c28aaSamw 
860*da6c28aaSamw 	/*
861*da6c28aaSamw 	 * Windows cannot handle an offset > SMB_EOF.
862*da6c28aaSamw 	 * Pretend we are at EOF.
863*da6c28aaSamw 	 */
864*da6c28aaSamw 
865*da6c28aaSamw 	if (auio.uio_loffset > SMB_EOF) {
866*da6c28aaSamw 		*count = 0;
867*da6c28aaSamw 		return (0);
868*da6c28aaSamw 	}
869*da6c28aaSamw 
870*da6c28aaSamw 	*count = *count - auio.uio_resid;
871*da6c28aaSamw 	return (0);
872*da6c28aaSamw }
873*da6c28aaSamw 
874*da6c28aaSamw /*
875*da6c28aaSamw  * smb_vop_readdir_entry()
876*da6c28aaSamw  *
877*da6c28aaSamw  * This function retrieves the first valid entry from the
878*da6c28aaSamw  * SMB_MINLEN_RDDIR_BUF-sized buffer returned by smb_vop_readdir_readpage()
879*da6c28aaSamw  * to smb_vop_readdir().
880*da6c28aaSamw  *
881*da6c28aaSamw  * Both dirent64_t and edirent_t structures need to be handled.  The former is
882*da6c28aaSamw  * needed for file systems that do not support VFSFT_DIRENTFLAGS.  The latter
883*da6c28aaSamw  * is required for proper handling of case collisions on file systems that
884*da6c28aaSamw  * support case-insensitivity.  edirent_t structures are also used for
885*da6c28aaSamw  * case-sensitive file systems if VFSFT_DIRENTFLAGS is supported.
886*da6c28aaSamw  */
887*da6c28aaSamw 
888*da6c28aaSamw static int
889*da6c28aaSamw smb_vop_readdir_entry(vnode_t *dvp, uint32_t *cookiep, char *name, int *namelen,
890*da6c28aaSamw     ino64_t *inop, vnode_t **vpp, char *od_name, int flags, cred_t *cr,
891*da6c28aaSamw     caller_context_t *ct, char *dirbuf, int num_bytes)
892*da6c28aaSamw {
893*da6c28aaSamw 	uint32_t next_cookie;
894*da6c28aaSamw 	int ebufsize;
895*da6c28aaSamw 	int error = 0;
896*da6c28aaSamw 	int len;
897*da6c28aaSamw 	int rc;
898*da6c28aaSamw 	char shortname[MANGLE_NAMELEN];
899*da6c28aaSamw 	char name83[MANGLE_NAMELEN];
900*da6c28aaSamw 	char *ebuf = NULL;
901*da6c28aaSamw 	edirent_t *edp;
902*da6c28aaSamw 	dirent64_t *dp = NULL;
903*da6c28aaSamw 	vnode_t *vp = NULL;
904*da6c28aaSamw 
905*da6c28aaSamw 	ASSERT(dirbuf);
906*da6c28aaSamw 
907*da6c28aaSamw 	/*
908*da6c28aaSamw 	 * Use edirent_t structure for both
909*da6c28aaSamw 	 * entflags not working for streams so don't try to use them
910*da6c28aaSamw 	 */
911*da6c28aaSamw 	if (!(flags & SMB_STREAM_RDDIR) &&
912*da6c28aaSamw 	    (vfs_has_feature(dvp->v_vfsp, VFSFT_DIRENTFLAGS))) {
913*da6c28aaSamw 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
914*da6c28aaSamw 		edp = (edirent_t *)dirbuf;
915*da6c28aaSamw 	} else {
916*da6c28aaSamw 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
917*da6c28aaSamw 		dp = (dirent64_t *)dirbuf;
918*da6c28aaSamw 		ebufsize = EDIRENT_RECLEN(MAXNAMELEN);
919*da6c28aaSamw 		ebuf = kmem_zalloc(ebufsize, KM_SLEEP);
920*da6c28aaSamw 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
921*da6c28aaSamw 		edp = (edirent_t *)ebuf;
922*da6c28aaSamw 	}
923*da6c28aaSamw 
924*da6c28aaSamw 	while (edp) {
925*da6c28aaSamw 		if (dp)
926*da6c28aaSamw 			DP_TO_EDP(dp, edp);
927*da6c28aaSamw 
928*da6c28aaSamw 		next_cookie = (uint32_t)edp->ed_off;
929*da6c28aaSamw 		if (edp->ed_ino == 0) {
930*da6c28aaSamw 			*cookiep = next_cookie;
931*da6c28aaSamw 
932*da6c28aaSamw 			if (dp) {
933*da6c28aaSamw 				/*LINTED E_BAD_PTR_CAST_ALIGN*/
934*da6c28aaSamw 				DP_ADVANCE(dp, dirbuf, num_bytes);
935*da6c28aaSamw 				if (dp == NULL)
936*da6c28aaSamw 					edp = NULL;
937*da6c28aaSamw 			} else {
938*da6c28aaSamw 				/*LINTED E_BAD_PTR_CAST_ALIGN*/
939*da6c28aaSamw 				EDP_ADVANCE(edp, dirbuf, num_bytes);
940*da6c28aaSamw 			}
941*da6c28aaSamw 			continue;
942*da6c28aaSamw 		}
943*da6c28aaSamw 
944*da6c28aaSamw 		len = strlen(edp->ed_name);
945*da6c28aaSamw 
946*da6c28aaSamw 		if (*namelen < len) {
947*da6c28aaSamw 			*namelen = 0;
948*da6c28aaSamw 
949*da6c28aaSamw 			if (ebuf)
950*da6c28aaSamw 				kmem_free(ebuf, ebufsize);
951*da6c28aaSamw 
952*da6c28aaSamw 			return (EOVERFLOW);
953*da6c28aaSamw 		}
954*da6c28aaSamw 
955*da6c28aaSamw 		/*
956*da6c28aaSamw 		 * Do not pass SMB_IGNORE_CASE to smb_vop_lookup
957*da6c28aaSamw 		 */
958*da6c28aaSamw 
959*da6c28aaSamw 		error = smb_vop_lookup(dvp, edp->ed_name, vpp ? vpp : &vp,
960*da6c28aaSamw 		    od_name, 0, NULL, cr, ct);
961*da6c28aaSamw 
962*da6c28aaSamw 		if (error) {
963*da6c28aaSamw 			if (error == ENOENT) {
964*da6c28aaSamw 				*cookiep = (uint32_t)next_cookie;
965*da6c28aaSamw 
966*da6c28aaSamw 				if (dp) {
967*da6c28aaSamw 					/*LINTED E_BAD_PTR_CAST_ALIGN*/
968*da6c28aaSamw 					DP_ADVANCE(dp, dirbuf, num_bytes);
969*da6c28aaSamw 					if (dp == NULL)
970*da6c28aaSamw 						edp = NULL;
971*da6c28aaSamw 				} else {
972*da6c28aaSamw 					/*LINTED E_BAD_PTR_CAST_ALIGN*/
973*da6c28aaSamw 					EDP_ADVANCE(edp, dirbuf, num_bytes);
974*da6c28aaSamw 				}
975*da6c28aaSamw 				continue;
976*da6c28aaSamw 			}
977*da6c28aaSamw 
978*da6c28aaSamw 
979*da6c28aaSamw 			*namelen = 0;
980*da6c28aaSamw 
981*da6c28aaSamw 			if (ebuf)
982*da6c28aaSamw 				kmem_free(ebuf, ebufsize);
983*da6c28aaSamw 
984*da6c28aaSamw 			return (error);
985*da6c28aaSamw 		}
986*da6c28aaSamw 
987*da6c28aaSamw 		if ((flags & SMB_IGNORE_CASE) && ED_CASE_CONFLICTS(edp)) {
988*da6c28aaSamw 			rc = smb_mangle_name(edp->ed_ino, edp->ed_name,
989*da6c28aaSamw 			    shortname, name83, 1);
990*da6c28aaSamw 
991*da6c28aaSamw 			if (rc == 1) { /* success */
992*da6c28aaSamw 				(void) strlcpy(name, shortname, *namelen + 1);
993*da6c28aaSamw 				*namelen = strlen(shortname);
994*da6c28aaSamw 			} else {
995*da6c28aaSamw 				(void) strlcpy(name, edp->ed_name,
996*da6c28aaSamw 				    *namelen + 1);
997*da6c28aaSamw 				name[*namelen] = '\0';
998*da6c28aaSamw 			}
999*da6c28aaSamw 
1000*da6c28aaSamw 		} else {
1001*da6c28aaSamw 			(void) strlcpy(name, edp->ed_name, *namelen + 1);
1002*da6c28aaSamw 				*namelen = len;
1003*da6c28aaSamw 		}
1004*da6c28aaSamw 
1005*da6c28aaSamw 		if (vpp == NULL)
1006*da6c28aaSamw 			VN_RELE(vp);
1007*da6c28aaSamw 
1008*da6c28aaSamw 		if (inop)
1009*da6c28aaSamw 			*inop = edp->ed_ino;
1010*da6c28aaSamw 
1011*da6c28aaSamw 		*cookiep = (uint32_t)next_cookie;
1012*da6c28aaSamw 		break;
1013*da6c28aaSamw 	}
1014*da6c28aaSamw 
1015*da6c28aaSamw 	if (ebuf)
1016*da6c28aaSamw 		kmem_free(ebuf, ebufsize);
1017*da6c28aaSamw 
1018*da6c28aaSamw 	return (error);
1019*da6c28aaSamw }
1020*da6c28aaSamw 
1021*da6c28aaSamw /*
1022*da6c28aaSamw  * smb_sa_to_va_mask
1023*da6c28aaSamw  *
1024*da6c28aaSamw  * Set va_mask by running through the SMB_AT_* #define's and
1025*da6c28aaSamw  * setting those bits that correspond to the SMB_AT_* bits
1026*da6c28aaSamw  * set in sa_mask.
1027*da6c28aaSamw  */
1028*da6c28aaSamw 
1029*da6c28aaSamw void
1030*da6c28aaSamw smb_sa_to_va_mask(uint_t sa_mask, uint_t *va_maskp)
1031*da6c28aaSamw {
1032*da6c28aaSamw 	int i;
1033*da6c28aaSamw 	uint_t smask;
1034*da6c28aaSamw 
1035*da6c28aaSamw 	smask = (sa_mask);
1036*da6c28aaSamw 	for (i = SMB_AT_TYPE; (i < SMB_AT_MAX) && (smask != 0); ++i) {
1037*da6c28aaSamw 		if (smask & 1)
1038*da6c28aaSamw 			*(va_maskp) |= smb_attrmap[i];
1039*da6c28aaSamw 
1040*da6c28aaSamw 		smask >>= 1;
1041*da6c28aaSamw 	}
1042*da6c28aaSamw }
1043*da6c28aaSamw 
1044*da6c28aaSamw /*
1045*da6c28aaSamw  * smb_vop_getdents()
1046*da6c28aaSamw  *
1047*da6c28aaSamw  * Upon success, the smb_node corresponding to each entry returned will
1048*da6c28aaSamw  * have a reference taken on it.  These will be released in
1049*da6c28aaSamw  * smb_trans2_find_get_dents().
1050*da6c28aaSamw  *
1051*da6c28aaSamw  * If an error is returned from this routine, a list of already processed
1052*da6c28aaSamw  * entries will be returned.  The smb_nodes corresponding to these entries
1053*da6c28aaSamw  * will be referenced, and will be released in smb_trans2_find_get_dents().
1054*da6c28aaSamw  *
1055*da6c28aaSamw  * The returned dp->d_name field will contain either the on-disk name or, if
1056*da6c28aaSamw  * it needs mangling or has a case-insensitive collision, the mangled
1057*da6c28aaSamw  * "shortname."  In this case, the on-disk name can be retrieved from the
1058*da6c28aaSamw  * smb_node's od_name (the smb_node is passed to smb_gather_dents_info()).
1059*da6c28aaSamw  */
1060*da6c28aaSamw 
1061*da6c28aaSamw int /*ARGSUSED*/
1062*da6c28aaSamw smb_vop_getdents(
1063*da6c28aaSamw     smb_node_t		*dir_snode,
1064*da6c28aaSamw     uint32_t		*cookiep,
1065*da6c28aaSamw     uint64_t		*verifierp,
1066*da6c28aaSamw     int32_t		*dircountp,
1067*da6c28aaSamw     char		*arg,
1068*da6c28aaSamw     char		*pattern,
1069*da6c28aaSamw     uint32_t		flags,
1070*da6c28aaSamw     smb_request_t	*sr,
1071*da6c28aaSamw     cred_t		*cr,
1072*da6c28aaSamw     caller_context_t	*ct)
1073*da6c28aaSamw {
1074*da6c28aaSamw 	int		error = 0;
1075*da6c28aaSamw 	int		maxentries;
1076*da6c28aaSamw 	int		num_bytes;
1077*da6c28aaSamw 	int		resid;
1078*da6c28aaSamw 	char		*dirbuf = NULL;
1079*da6c28aaSamw 	vnode_t		*dvp;
1080*da6c28aaSamw 	/*LINTED E_BAD_PTR_CAST_ALIGN*/
1081*da6c28aaSamw 	smb_dent_info_hdr_t *ihdr = (smb_dent_info_hdr_t *)arg;
1082*da6c28aaSamw 
1083*da6c28aaSamw 	dvp = dir_snode->vp;
1084*da6c28aaSamw 
1085*da6c28aaSamw 	resid = ihdr->uio.uio_resid;
1086*da6c28aaSamw 	maxentries = resid / SMB_MAX_DENT_INFO_SIZE;
1087*da6c28aaSamw 
1088*da6c28aaSamw 	bzero(ihdr->iov->iov_base, resid);
1089*da6c28aaSamw 
1090*da6c28aaSamw 	dirbuf = kmem_alloc(SMB_MINLEN_RDDIR_BUF, KM_SLEEP);
1091*da6c28aaSamw 
1092*da6c28aaSamw 	while (maxentries) {
1093*da6c28aaSamw 
1094*da6c28aaSamw 		bzero(dirbuf, SMB_MINLEN_RDDIR_BUF);
1095*da6c28aaSamw 
1096*da6c28aaSamw 		num_bytes = SMB_MINLEN_RDDIR_BUF;
1097*da6c28aaSamw 		error = smb_vop_readdir_readpage(dvp, dirbuf, *cookiep,
1098*da6c28aaSamw 		    &num_bytes, cr, ct, flags);
1099*da6c28aaSamw 
1100*da6c28aaSamw 		if (error || (num_bytes <= 0))
1101*da6c28aaSamw 			break;
1102*da6c28aaSamw 
1103*da6c28aaSamw 		error = smb_vop_getdents_entries(dir_snode, cookiep, dircountp,
1104*da6c28aaSamw 		    arg, flags, sr, cr, ct, dirbuf, &maxentries, num_bytes,
1105*da6c28aaSamw 		    pattern);
1106*da6c28aaSamw 
1107*da6c28aaSamw 		if (error)
1108*da6c28aaSamw 			goto out;
1109*da6c28aaSamw 	}
1110*da6c28aaSamw 
1111*da6c28aaSamw 	if (num_bytes < 0) {
1112*da6c28aaSamw 		error = -1;
1113*da6c28aaSamw 	} else if (num_bytes == 0) {
1114*da6c28aaSamw 		*cookiep = SMB_EOF;
1115*da6c28aaSamw 		error = 0;
1116*da6c28aaSamw 	} else {
1117*da6c28aaSamw 		error = 0;
1118*da6c28aaSamw 	}
1119*da6c28aaSamw 
1120*da6c28aaSamw out:
1121*da6c28aaSamw 	if (dirbuf)
1122*da6c28aaSamw 		kmem_free(dirbuf, SMB_MINLEN_RDDIR_BUF);
1123*da6c28aaSamw 
1124*da6c28aaSamw 	return (error);
1125*da6c28aaSamw }
1126*da6c28aaSamw 
1127*da6c28aaSamw /*
1128*da6c28aaSamw  * smb_vop_getdents_entries()
1129*da6c28aaSamw  *
1130*da6c28aaSamw  * This function retrieves names from the SMB_MINLEN_RDDIR_BUF-sized buffer
1131*da6c28aaSamw  * returned by smb_vop_readdir_readpage() to smb_vop_getdents().
1132*da6c28aaSamw  *
1133*da6c28aaSamw  * Both dirent64_t and edirent_t structures need to be handled.  The former is
1134*da6c28aaSamw  * needed for file systems that do not support VFSFT_DIRENTFLAGS.  The latter
1135*da6c28aaSamw  * is required for properly handling case collisions on file systems that
1136*da6c28aaSamw  * support case-insensitivity.  edirent_t is also used on case-sensitive
1137*da6c28aaSamw  * file systems where VFSFT_DIRENTFLAGS is available.
1138*da6c28aaSamw  */
1139*da6c28aaSamw 
1140*da6c28aaSamw static int
1141*da6c28aaSamw smb_vop_getdents_entries(
1142*da6c28aaSamw     smb_node_t		*dir_snode,
1143*da6c28aaSamw     uint32_t		*cookiep,
1144*da6c28aaSamw     int32_t		*dircountp,
1145*da6c28aaSamw     char		*arg,
1146*da6c28aaSamw     uint32_t		flags,
1147*da6c28aaSamw     struct smb_request	*sr,
1148*da6c28aaSamw     cred_t		*cr,
1149*da6c28aaSamw     caller_context_t	*ct,
1150*da6c28aaSamw     char		*dirbuf,
1151*da6c28aaSamw     int			*maxentries,
1152*da6c28aaSamw     int			num_bytes,
1153*da6c28aaSamw     char		*pattern)
1154*da6c28aaSamw {
1155*da6c28aaSamw 	uint32_t	next_cookie;
1156*da6c28aaSamw 	int		ebufsize;
1157*da6c28aaSamw 	char		*tmp_name;
1158*da6c28aaSamw 	int		error;
1159*da6c28aaSamw 	int		rc;
1160*da6c28aaSamw 	char		shortname[MANGLE_NAMELEN];
1161*da6c28aaSamw 	char		name83[MANGLE_NAMELEN];
1162*da6c28aaSamw 	char		*ebuf = NULL;
1163*da6c28aaSamw 	dirent64_t	*dp = NULL;
1164*da6c28aaSamw 	edirent_t	*edp;
1165*da6c28aaSamw 	smb_node_t	*ret_snode;
1166*da6c28aaSamw 	smb_attr_t	ret_attr;
1167*da6c28aaSamw 	vnode_t		*dvp;
1168*da6c28aaSamw 	vnode_t		*fvp;
1169*da6c28aaSamw 
1170*da6c28aaSamw 	ASSERT(dirbuf);
1171*da6c28aaSamw 
1172*da6c28aaSamw 	dvp = dir_snode->vp;
1173*da6c28aaSamw 
1174*da6c28aaSamw 	if (vfs_has_feature(dvp->v_vfsp, VFSFT_DIRENTFLAGS)) {
1175*da6c28aaSamw 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
1176*da6c28aaSamw 		edp = (edirent_t *)dirbuf;
1177*da6c28aaSamw 	} else {
1178*da6c28aaSamw 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
1179*da6c28aaSamw 		dp = (dirent64_t *)dirbuf;
1180*da6c28aaSamw 		ebufsize = EDIRENT_RECLEN(MAXNAMELEN);
1181*da6c28aaSamw 		ebuf = kmem_zalloc(ebufsize, KM_SLEEP);
1182*da6c28aaSamw 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
1183*da6c28aaSamw 		edp = (edirent_t *)ebuf;
1184*da6c28aaSamw 	}
1185*da6c28aaSamw 
1186*da6c28aaSamw 	while (edp) {
1187*da6c28aaSamw 		if (dp)
1188*da6c28aaSamw 			DP_TO_EDP(dp, edp);
1189*da6c28aaSamw 
1190*da6c28aaSamw 		if (*maxentries == 0)
1191*da6c28aaSamw 			break;
1192*da6c28aaSamw 
1193*da6c28aaSamw 		next_cookie = (uint32_t)edp->ed_off;
1194*da6c28aaSamw 
1195*da6c28aaSamw 		if (edp->ed_ino == 0) {
1196*da6c28aaSamw 			*cookiep = next_cookie;
1197*da6c28aaSamw 			if (dp) {
1198*da6c28aaSamw 				/*LINTED E_BAD_PTR_CAST_ALIGN*/
1199*da6c28aaSamw 				DP_ADVANCE(dp, dirbuf, num_bytes);
1200*da6c28aaSamw 				if (dp == NULL)
1201*da6c28aaSamw 					edp = NULL;
1202*da6c28aaSamw 			} else {
1203*da6c28aaSamw 				/*LINTED E_BAD_PTR_CAST_ALIGN*/
1204*da6c28aaSamw 				EDP_ADVANCE(edp, dirbuf, num_bytes);
1205*da6c28aaSamw 			}
1206*da6c28aaSamw 			continue;
1207*da6c28aaSamw 		}
1208*da6c28aaSamw 
1209*da6c28aaSamw 		error = smb_vop_lookup(dvp, edp->ed_name, &fvp,
1210*da6c28aaSamw 		    NULL, 0, NULL, cr, ct);
1211*da6c28aaSamw 
1212*da6c28aaSamw 		if (error) {
1213*da6c28aaSamw 			if (error == ENOENT) {
1214*da6c28aaSamw 				*cookiep = next_cookie;
1215*da6c28aaSamw 				if (dp) {
1216*da6c28aaSamw 					/*LINTED E_BAD_PTR_CAST_ALIGN*/
1217*da6c28aaSamw 					DP_ADVANCE(dp, dirbuf,
1218*da6c28aaSamw 					    num_bytes);
1219*da6c28aaSamw 					if (dp == NULL)
1220*da6c28aaSamw 						edp = NULL;
1221*da6c28aaSamw 				} else {
1222*da6c28aaSamw 					/*LINTED E_BAD_PTR_CAST_ALIGN*/
1223*da6c28aaSamw 					EDP_ADVANCE(edp, dirbuf,
1224*da6c28aaSamw 					    num_bytes);
1225*da6c28aaSamw 				}
1226*da6c28aaSamw 				continue;
1227*da6c28aaSamw 			}
1228*da6c28aaSamw 			if (ebuf)
1229*da6c28aaSamw 				kmem_free(ebuf, ebufsize);
1230*da6c28aaSamw 
1231*da6c28aaSamw 			return (error);
1232*da6c28aaSamw 		}
1233*da6c28aaSamw 
1234*da6c28aaSamw 		ret_snode = smb_node_lookup(sr, NULL, cr, fvp,
1235*da6c28aaSamw 		    edp->ed_name, dir_snode, NULL, &ret_attr);
1236*da6c28aaSamw 
1237*da6c28aaSamw 		if (ret_snode == NULL) {
1238*da6c28aaSamw 			VN_RELE(fvp);
1239*da6c28aaSamw 
1240*da6c28aaSamw 			if (ebuf)
1241*da6c28aaSamw 				kmem_free(ebuf, ebufsize);
1242*da6c28aaSamw 
1243*da6c28aaSamw 			return (ENOMEM);
1244*da6c28aaSamw 		}
1245*da6c28aaSamw 
1246*da6c28aaSamw 		if (smb_match_name(edp->ed_ino, edp->ed_name, shortname,
1247*da6c28aaSamw 		    name83, pattern, (flags & SMB_IGNORE_CASE))) {
1248*da6c28aaSamw 
1249*da6c28aaSamw 			tmp_name = edp->ed_name;
1250*da6c28aaSamw 
1251*da6c28aaSamw 			if ((flags & SMB_IGNORE_CASE) &&
1252*da6c28aaSamw 			    ED_CASE_CONFLICTS(edp)) {
1253*da6c28aaSamw 				rc = smb_mangle_name(edp->ed_ino, edp->ed_name,
1254*da6c28aaSamw 				    shortname, name83, 1);
1255*da6c28aaSamw 				if (rc == 1)
1256*da6c28aaSamw 					tmp_name = shortname;
1257*da6c28aaSamw 			} else {
1258*da6c28aaSamw 				rc = smb_mangle_name(edp->ed_ino, edp->ed_name,
1259*da6c28aaSamw 				    shortname, name83, 0);
1260*da6c28aaSamw 			}
1261*da6c28aaSamw 
1262*da6c28aaSamw 			if (rc != 1) {
1263*da6c28aaSamw 				(void) strlcpy(shortname, edp->ed_name,
1264*da6c28aaSamw 				    MANGLE_NAMELEN);
1265*da6c28aaSamw 				(void) strlcpy(name83, edp->ed_name,
1266*da6c28aaSamw 				    MANGLE_NAMELEN);
1267*da6c28aaSamw 				shortname[MANGLE_NAMELEN - 1] = '\0';
1268*da6c28aaSamw 				name83[MANGLE_NAMELEN - 1] = '\0';
1269*da6c28aaSamw 			}
1270*da6c28aaSamw 
1271*da6c28aaSamw 			error = smb_gather_dents_info(arg, edp->ed_ino,
1272*da6c28aaSamw 			    strlen(tmp_name), tmp_name, next_cookie, dircountp,
1273*da6c28aaSamw 			    &ret_attr, ret_snode, shortname, name83);
1274*da6c28aaSamw 
1275*da6c28aaSamw 			if (error > 0) {
1276*da6c28aaSamw 				if (ebuf)
1277*da6c28aaSamw 					kmem_free(ebuf, ebufsize);
1278*da6c28aaSamw 				return (error);
1279*da6c28aaSamw 			}
1280*da6c28aaSamw 
1281*da6c28aaSamw 			/*
1282*da6c28aaSamw 			 * Treat errors from smb_gather_dents_info() that are
1283*da6c28aaSamw 			 * < 0 the same as EOF.
1284*da6c28aaSamw 			 */
1285*da6c28aaSamw 			if (error < 0) {
1286*da6c28aaSamw 				if (ebuf)
1287*da6c28aaSamw 					kmem_free(ebuf, ebufsize);
1288*da6c28aaSamw 				*maxentries = 0;
1289*da6c28aaSamw 				return (0);
1290*da6c28aaSamw 			}
1291*da6c28aaSamw 			(*maxentries)--;
1292*da6c28aaSamw 		} else {
1293*da6c28aaSamw 			smb_node_release(ret_snode);
1294*da6c28aaSamw 		}
1295*da6c28aaSamw 
1296*da6c28aaSamw 		*cookiep = next_cookie;
1297*da6c28aaSamw 
1298*da6c28aaSamw 		if (dp) {
1299*da6c28aaSamw 			/*LINTED E_BAD_PTR_CAST_ALIGN*/
1300*da6c28aaSamw 			DP_ADVANCE(dp, dirbuf, num_bytes);
1301*da6c28aaSamw 			if (dp == NULL)
1302*da6c28aaSamw 				edp = NULL;
1303*da6c28aaSamw 		} else {
1304*da6c28aaSamw 			/*LINTED E_BAD_PTR_CAST_ALIGN*/
1305*da6c28aaSamw 			EDP_ADVANCE(edp, dirbuf, num_bytes);
1306*da6c28aaSamw 		}
1307*da6c28aaSamw 	}
1308*da6c28aaSamw 
1309*da6c28aaSamw 	if (ebuf)
1310*da6c28aaSamw 		kmem_free(ebuf, ebufsize);
1311*da6c28aaSamw 
1312*da6c28aaSamw 	return (0);
1313*da6c28aaSamw }
1314*da6c28aaSamw 
1315*da6c28aaSamw /*
1316*da6c28aaSamw  * smb_vop_stream_lookup()
1317*da6c28aaSamw  *
1318*da6c28aaSamw  * The name returned in od_name is the on-disk name of the stream with the
1319*da6c28aaSamw  * SMB_STREAM_PREFIX stripped off.  od_name should be allocated to MAXNAMELEN
1320*da6c28aaSamw  * by the caller.
1321*da6c28aaSamw  */
1322*da6c28aaSamw 
1323*da6c28aaSamw int
1324*da6c28aaSamw smb_vop_stream_lookup(vnode_t *fvp, char *stream_name, vnode_t **vpp,
1325*da6c28aaSamw     char *od_name, vnode_t **xattrdirvpp, int flags, vnode_t *rootvp,
1326*da6c28aaSamw     cred_t *cr, caller_context_t *ct)
1327*da6c28aaSamw {
1328*da6c28aaSamw 	char *solaris_stream_name;
1329*da6c28aaSamw 	char *name;
1330*da6c28aaSamw 	int error;
1331*da6c28aaSamw 
1332*da6c28aaSamw 	if ((error = smb_vop_lookup_xattrdir(fvp, xattrdirvpp,
1333*da6c28aaSamw 	    LOOKUP_XATTR | CREATE_XATTR_DIR, cr, ct)) != 0)
1334*da6c28aaSamw 		return (error);
1335*da6c28aaSamw 
1336*da6c28aaSamw 	/*
1337*da6c28aaSamw 	 * Prepend SMB_STREAM_PREFIX to stream name
1338*da6c28aaSamw 	 */
1339*da6c28aaSamw 
1340*da6c28aaSamw 	solaris_stream_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
1341*da6c28aaSamw 	(void) sprintf(solaris_stream_name, "%s%s", SMB_STREAM_PREFIX,
1342*da6c28aaSamw 	    stream_name);
1343*da6c28aaSamw 
1344*da6c28aaSamw 	/*
1345*da6c28aaSamw 	 * "name" will hold the on-disk name returned from smb_vop_lookup
1346*da6c28aaSamw 	 * for the stream, including the SMB_STREAM_PREFIX.
1347*da6c28aaSamw 	 */
1348*da6c28aaSamw 
1349*da6c28aaSamw 	name = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
1350*da6c28aaSamw 
1351*da6c28aaSamw 	if ((error = smb_vop_lookup(*xattrdirvpp, solaris_stream_name, vpp,
1352*da6c28aaSamw 	    name, flags, rootvp, cr, ct)) != 0) {
1353*da6c28aaSamw 		VN_RELE(*xattrdirvpp);
1354*da6c28aaSamw 	} else {
1355*da6c28aaSamw 		(void) strlcpy(od_name, &(name[SMB_STREAM_PREFIX_LEN]),
1356*da6c28aaSamw 		    MAXNAMELEN);
1357*da6c28aaSamw 	}
1358*da6c28aaSamw 
1359*da6c28aaSamw 	kmem_free(solaris_stream_name, MAXNAMELEN);
1360*da6c28aaSamw 	kmem_free(name, MAXNAMELEN);
1361*da6c28aaSamw 
1362*da6c28aaSamw 	return (error);
1363*da6c28aaSamw }
1364*da6c28aaSamw 
1365*da6c28aaSamw int
1366*da6c28aaSamw smb_vop_stream_create(vnode_t *fvp, char *stream_name, smb_attr_t *attr,
1367*da6c28aaSamw     vnode_t **vpp, vnode_t **xattrdirvpp, int flags, cred_t *cr,
1368*da6c28aaSamw     caller_context_t *ct)
1369*da6c28aaSamw {
1370*da6c28aaSamw 	char *solaris_stream_name;
1371*da6c28aaSamw 	int error;
1372*da6c28aaSamw 
1373*da6c28aaSamw 	if ((error = smb_vop_lookup_xattrdir(fvp, xattrdirvpp,
1374*da6c28aaSamw 	    LOOKUP_XATTR | CREATE_XATTR_DIR, cr, ct)) != 0)
1375*da6c28aaSamw 		return (error);
1376*da6c28aaSamw 
1377*da6c28aaSamw 	/*
1378*da6c28aaSamw 	 * Prepend SMB_STREAM_PREFIX to stream name
1379*da6c28aaSamw 	 */
1380*da6c28aaSamw 
1381*da6c28aaSamw 	solaris_stream_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
1382*da6c28aaSamw 	(void) sprintf(solaris_stream_name, "%s%s", SMB_STREAM_PREFIX,
1383*da6c28aaSamw 	    stream_name);
1384*da6c28aaSamw 
1385*da6c28aaSamw 	if ((error = smb_vop_create(*xattrdirvpp, solaris_stream_name, attr,
1386*da6c28aaSamw 	    vpp, flags, cr, ct, NULL)) != 0)
1387*da6c28aaSamw 		VN_RELE(*xattrdirvpp);
1388*da6c28aaSamw 
1389*da6c28aaSamw 	kmem_free(solaris_stream_name, MAXNAMELEN);
1390*da6c28aaSamw 
1391*da6c28aaSamw 	return (error);
1392*da6c28aaSamw }
1393*da6c28aaSamw 
1394*da6c28aaSamw int
1395*da6c28aaSamw smb_vop_stream_remove(vnode_t *vp, char *stream_name, int flags, cred_t *cr,
1396*da6c28aaSamw     caller_context_t *ct)
1397*da6c28aaSamw {
1398*da6c28aaSamw 	char *solaris_stream_name;
1399*da6c28aaSamw 	vnode_t *xattrdirvp;
1400*da6c28aaSamw 	int error;
1401*da6c28aaSamw 
1402*da6c28aaSamw 	if ((error = smb_vop_lookup_xattrdir(vp, &xattrdirvp, LOOKUP_XATTR, cr,
1403*da6c28aaSamw 	    ct)) != 0)
1404*da6c28aaSamw 		return (error);
1405*da6c28aaSamw 
1406*da6c28aaSamw 	/*
1407*da6c28aaSamw 	 * Prepend SMB_STREAM_PREFIX to stream name
1408*da6c28aaSamw 	 */
1409*da6c28aaSamw 
1410*da6c28aaSamw 	solaris_stream_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
1411*da6c28aaSamw 	(void) sprintf(solaris_stream_name, "%s%s", SMB_STREAM_PREFIX,
1412*da6c28aaSamw 	    stream_name);
1413*da6c28aaSamw 
1414*da6c28aaSamw 	/* XXX might have to use kcred */
1415*da6c28aaSamw 	error = smb_vop_remove(xattrdirvp, solaris_stream_name, flags, cr, ct);
1416*da6c28aaSamw 
1417*da6c28aaSamw 	kmem_free(solaris_stream_name, MAXNAMELEN);
1418*da6c28aaSamw 
1419*da6c28aaSamw 	return (error);
1420*da6c28aaSamw }
1421*da6c28aaSamw 
1422*da6c28aaSamw /*
1423*da6c28aaSamw  * smb_vop_stream_readdir()
1424*da6c28aaSamw  *
1425*da6c28aaSamw  * Note: stream_info.size is not filled in in this routine.
1426*da6c28aaSamw  * It needs to be filled in by the caller due to the parameters for getattr.
1427*da6c28aaSamw  *
1428*da6c28aaSamw  * stream_info.name is set to the on-disk stream name with the SMB_STREAM_PREFIX
1429*da6c28aaSamw  * removed.
1430*da6c28aaSamw  */
1431*da6c28aaSamw 
1432*da6c28aaSamw int
1433*da6c28aaSamw smb_vop_stream_readdir(vnode_t *fvp, uint32_t *cookiep,
1434*da6c28aaSamw     struct fs_stream_info *stream_info, vnode_t **vpp, vnode_t **xattrdirvpp,
1435*da6c28aaSamw     int flags, cred_t *cr, caller_context_t *ct)
1436*da6c28aaSamw {
1437*da6c28aaSamw 	int nsize = MAXNAMELEN-1;
1438*da6c28aaSamw 	int error = 0;
1439*da6c28aaSamw 	ino64_t ino;
1440*da6c28aaSamw 	char *tmp_name;
1441*da6c28aaSamw 	vnode_t *xattrdirvp;
1442*da6c28aaSamw 	vnode_t *vp;
1443*da6c28aaSamw 
1444*da6c28aaSamw 	if ((error = smb_vop_lookup_xattrdir(fvp, &xattrdirvp, LOOKUP_XATTR,
1445*da6c28aaSamw 	    cr, ct)) != 0)
1446*da6c28aaSamw 		return (error);
1447*da6c28aaSamw 
1448*da6c28aaSamw 	bzero(stream_info->name, sizeof (stream_info->name));
1449*da6c28aaSamw 	stream_info->size = 0;
1450*da6c28aaSamw 
1451*da6c28aaSamw 	tmp_name = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
1452*da6c28aaSamw 
1453*da6c28aaSamw 	for (;;) {
1454*da6c28aaSamw 		error = smb_vop_readdir(xattrdirvp, cookiep, tmp_name, &nsize,
1455*da6c28aaSamw 		    &ino, &vp, NULL, flags | SMB_STREAM_RDDIR, cr, ct);
1456*da6c28aaSamw 
1457*da6c28aaSamw 		if (error || (*cookiep == SMB_EOF))
1458*da6c28aaSamw 			break;
1459*da6c28aaSamw 
1460*da6c28aaSamw 		if (strncmp(tmp_name, SMB_STREAM_PREFIX,
1461*da6c28aaSamw 		    SMB_STREAM_PREFIX_LEN)) {
1462*da6c28aaSamw 			VN_RELE(vp);
1463*da6c28aaSamw 			continue;
1464*da6c28aaSamw 		}
1465*da6c28aaSamw 
1466*da6c28aaSamw 		tmp_name[nsize] = '\0';
1467*da6c28aaSamw 		(void) strlcpy(stream_info->name,
1468*da6c28aaSamw 		    &(tmp_name[SMB_STREAM_PREFIX_LEN]),
1469*da6c28aaSamw 		    sizeof (stream_info->name));
1470*da6c28aaSamw 
1471*da6c28aaSamw 		nsize -= SMB_STREAM_PREFIX_LEN;
1472*da6c28aaSamw 		break;
1473*da6c28aaSamw 	}
1474*da6c28aaSamw 
1475*da6c28aaSamw 	if ((error == 0) && nsize) {
1476*da6c28aaSamw 		if (vpp)
1477*da6c28aaSamw 			*vpp = vp;
1478*da6c28aaSamw 		else
1479*da6c28aaSamw 			VN_RELE(vp);
1480*da6c28aaSamw 
1481*da6c28aaSamw 		if (xattrdirvpp)
1482*da6c28aaSamw 			*xattrdirvpp = xattrdirvp;
1483*da6c28aaSamw 		else
1484*da6c28aaSamw 			VN_RELE(xattrdirvp);
1485*da6c28aaSamw 
1486*da6c28aaSamw 	}
1487*da6c28aaSamw 
1488*da6c28aaSamw 	kmem_free(tmp_name, MAXNAMELEN);
1489*da6c28aaSamw 
1490*da6c28aaSamw 	return (error);
1491*da6c28aaSamw }
1492*da6c28aaSamw 
1493*da6c28aaSamw int
1494*da6c28aaSamw smb_vop_lookup_xattrdir(vnode_t *fvp, vnode_t **xattrdirvpp, int flags,
1495*da6c28aaSamw     cred_t *cr, caller_context_t *ct)
1496*da6c28aaSamw {
1497*da6c28aaSamw 	int error;
1498*da6c28aaSamw 
1499*da6c28aaSamw 	error = VOP_LOOKUP(fvp, "", xattrdirvpp, NULL, flags, NULL, cr, ct,
1500*da6c28aaSamw 	    NULL, NULL);
1501*da6c28aaSamw 	return (error);
1502*da6c28aaSamw }
1503*da6c28aaSamw 
1504*da6c28aaSamw /*
1505*da6c28aaSamw  * smb_vop_traverse_check()
1506*da6c28aaSamw  *
1507*da6c28aaSamw  * This function checks to see if the passed-in vnode has a file system
1508*da6c28aaSamw  * mounted on it.  If it does, the mount point is "traversed" and the
1509*da6c28aaSamw  * vnode for the root of the file system is returned.
1510*da6c28aaSamw  */
1511*da6c28aaSamw 
1512*da6c28aaSamw int
1513*da6c28aaSamw smb_vop_traverse_check(vnode_t **vpp)
1514*da6c28aaSamw {
1515*da6c28aaSamw 	int error;
1516*da6c28aaSamw 
1517*da6c28aaSamw 	if (vn_mountedvfs(*vpp) == 0)
1518*da6c28aaSamw 		return (0);
1519*da6c28aaSamw 
1520*da6c28aaSamw 	/*
1521*da6c28aaSamw 	 * traverse() may return a different held vnode, even in the error case.
1522*da6c28aaSamw 	 * If it returns a different vnode, it will have released the original.
1523*da6c28aaSamw 	 */
1524*da6c28aaSamw 
1525*da6c28aaSamw 	error = traverse(vpp);
1526*da6c28aaSamw 
1527*da6c28aaSamw 	return (error);
1528*da6c28aaSamw }
1529*da6c28aaSamw 
1530*da6c28aaSamw int /*ARGSUSED*/
1531*da6c28aaSamw smb_vop_statfs(vnode_t *vp, struct statvfs64 *statp, cred_t *cr)
1532*da6c28aaSamw {
1533*da6c28aaSamw 	int error;
1534*da6c28aaSamw 
1535*da6c28aaSamw 	error = VFS_STATVFS(vp->v_vfsp, statp);
1536*da6c28aaSamw 
1537*da6c28aaSamw 	return (error);
1538*da6c28aaSamw }
1539*da6c28aaSamw 
1540*da6c28aaSamw /*
1541*da6c28aaSamw  * smb_vop_acl_from_vsa
1542*da6c28aaSamw  *
1543*da6c28aaSamw  * Converts given vsecattr_t structure to a acl_t structure.
1544*da6c28aaSamw  *
1545*da6c28aaSamw  * The allocated memory for retuned acl_t should be freed by
1546*da6c28aaSamw  * calling acl_free().
1547*da6c28aaSamw  */
1548*da6c28aaSamw static acl_t *
1549*da6c28aaSamw smb_vop_acl_from_vsa(vsecattr_t *vsecattr, acl_type_t acl_type)
1550*da6c28aaSamw {
1551*da6c28aaSamw 	int		aclbsize = 0;	/* size of acl list in bytes */
1552*da6c28aaSamw 	int		dfaclbsize = 0;	/* size of default acl list in bytes */
1553*da6c28aaSamw 	int		numacls;
1554*da6c28aaSamw 	acl_t		*acl_info;
1555*da6c28aaSamw 
1556*da6c28aaSamw 	ASSERT(vsecattr);
1557*da6c28aaSamw 
1558*da6c28aaSamw 	acl_info = acl_alloc(acl_type);
1559*da6c28aaSamw 	if (acl_info == NULL)
1560*da6c28aaSamw 		return (NULL);
1561*da6c28aaSamw 
1562*da6c28aaSamw 	acl_info->acl_flags = 0;
1563*da6c28aaSamw 
1564*da6c28aaSamw 	switch (acl_type) {
1565*da6c28aaSamw 
1566*da6c28aaSamw 	case ACLENT_T:
1567*da6c28aaSamw 		numacls = vsecattr->vsa_aclcnt + vsecattr->vsa_dfaclcnt;
1568*da6c28aaSamw 		aclbsize = vsecattr->vsa_aclcnt * sizeof (aclent_t);
1569*da6c28aaSamw 		dfaclbsize = vsecattr->vsa_dfaclcnt * sizeof (aclent_t);
1570*da6c28aaSamw 
1571*da6c28aaSamw 		acl_info->acl_cnt = numacls;
1572*da6c28aaSamw 		acl_info->acl_aclp = kmem_alloc(aclbsize + dfaclbsize,
1573*da6c28aaSamw 		    KM_SLEEP);
1574*da6c28aaSamw 		(void) memcpy(acl_info->acl_aclp, vsecattr->vsa_aclentp,
1575*da6c28aaSamw 		    aclbsize);
1576*da6c28aaSamw 		(void) memcpy((char *)acl_info->acl_aclp + aclbsize,
1577*da6c28aaSamw 		    vsecattr->vsa_dfaclentp, dfaclbsize);
1578*da6c28aaSamw 
1579*da6c28aaSamw 		if (acl_info->acl_cnt <= MIN_ACL_ENTRIES)
1580*da6c28aaSamw 			acl_info->acl_flags |= ACL_IS_TRIVIAL;
1581*da6c28aaSamw 
1582*da6c28aaSamw 		break;
1583*da6c28aaSamw 
1584*da6c28aaSamw 	case ACE_T:
1585*da6c28aaSamw 		aclbsize = vsecattr->vsa_aclcnt * sizeof (ace_t);
1586*da6c28aaSamw 		acl_info->acl_cnt = vsecattr->vsa_aclcnt;
1587*da6c28aaSamw 		acl_info->acl_flags = vsecattr->vsa_aclflags;
1588*da6c28aaSamw 		acl_info->acl_aclp = kmem_alloc(aclbsize, KM_SLEEP);
1589*da6c28aaSamw 		(void) memcpy(acl_info->acl_aclp, vsecattr->vsa_aclentp,
1590*da6c28aaSamw 		    aclbsize);
1591*da6c28aaSamw 		if (ace_trivial(acl_info->acl_aclp, acl_info->acl_cnt) == 0)
1592*da6c28aaSamw 			acl_info->acl_flags |= ACL_IS_TRIVIAL;
1593*da6c28aaSamw 
1594*da6c28aaSamw 		break;
1595*da6c28aaSamw 
1596*da6c28aaSamw 	default:
1597*da6c28aaSamw 		acl_free(acl_info);
1598*da6c28aaSamw 		return (NULL);
1599*da6c28aaSamw 	}
1600*da6c28aaSamw 
1601*da6c28aaSamw 	if (aclbsize && vsecattr->vsa_aclentp)
1602*da6c28aaSamw 		kmem_free(vsecattr->vsa_aclentp, aclbsize);
1603*da6c28aaSamw 	if (dfaclbsize && vsecattr->vsa_dfaclentp)
1604*da6c28aaSamw 		kmem_free(vsecattr->vsa_dfaclentp, dfaclbsize);
1605*da6c28aaSamw 
1606*da6c28aaSamw 	return (acl_info);
1607*da6c28aaSamw }
1608*da6c28aaSamw 
1609*da6c28aaSamw /*
1610*da6c28aaSamw  * smb_vop_acl_to_vsa
1611*da6c28aaSamw  *
1612*da6c28aaSamw  * Converts given acl_t structure to a vsecattr_t structure.
1613*da6c28aaSamw  *
1614*da6c28aaSamw  * IMPORTANT:
1615*da6c28aaSamw  * Upon successful return the memory allocated for vsa_aclentp
1616*da6c28aaSamw  * should be freed by calling kmem_free(). The size is returned
1617*da6c28aaSamw  * in aclbsize.
1618*da6c28aaSamw  */
1619*da6c28aaSamw int
1620*da6c28aaSamw smb_vop_acl_to_vsa(acl_t *acl_info, vsecattr_t *vsecattr, int *aclbsize)
1621*da6c28aaSamw {
1622*da6c28aaSamw 	int		error = 0;
1623*da6c28aaSamw 	int		numacls;
1624*da6c28aaSamw 	aclent_t	*aclp;
1625*da6c28aaSamw 
1626*da6c28aaSamw 	ASSERT(acl_info);
1627*da6c28aaSamw 	ASSERT(vsecattr);
1628*da6c28aaSamw 	ASSERT(aclbsize);
1629*da6c28aaSamw 
1630*da6c28aaSamw 	bzero(vsecattr, sizeof (vsecattr_t));
1631*da6c28aaSamw 	*aclbsize = 0;
1632*da6c28aaSamw 
1633*da6c28aaSamw 	switch (acl_info->acl_type) {
1634*da6c28aaSamw 	case ACLENT_T:
1635*da6c28aaSamw 		numacls = acl_info->acl_cnt;
1636*da6c28aaSamw 		/*
1637*da6c28aaSamw 		 * Minimum ACL size is three entries so might as well
1638*da6c28aaSamw 		 * bail out here.  Also limit request size to prevent user
1639*da6c28aaSamw 		 * from allocating too much kernel memory.  Maximum size
1640*da6c28aaSamw 		 * is MAX_ACL_ENTRIES for the ACL part and MAX_ACL_ENTRIES
1641*da6c28aaSamw 		 * for the default ACL part.
1642*da6c28aaSamw 		 */
1643*da6c28aaSamw 		if (numacls < 3 || numacls > (MAX_ACL_ENTRIES * 2)) {
1644*da6c28aaSamw 			error = EINVAL;
1645*da6c28aaSamw 			break;
1646*da6c28aaSamw 		}
1647*da6c28aaSamw 
1648*da6c28aaSamw 		vsecattr->vsa_mask = VSA_ACL;
1649*da6c28aaSamw 
1650*da6c28aaSamw 		vsecattr->vsa_aclcnt = numacls;
1651*da6c28aaSamw 		*aclbsize = numacls * sizeof (aclent_t);
1652*da6c28aaSamw 		vsecattr->vsa_aclentp = kmem_alloc(*aclbsize, KM_SLEEP);
1653*da6c28aaSamw 		(void) memcpy(vsecattr->vsa_aclentp, acl_info->acl_aclp,
1654*da6c28aaSamw 		    *aclbsize);
1655*da6c28aaSamw 
1656*da6c28aaSamw 		/* Sort the acl list */
1657*da6c28aaSamw 		ksort((caddr_t)vsecattr->vsa_aclentp,
1658*da6c28aaSamw 		    vsecattr->vsa_aclcnt, sizeof (aclent_t), cmp2acls);
1659*da6c28aaSamw 
1660*da6c28aaSamw 		/* Break into acl and default acl lists */
1661*da6c28aaSamw 		for (numacls = 0, aclp = vsecattr->vsa_aclentp;
1662*da6c28aaSamw 		    numacls < vsecattr->vsa_aclcnt;
1663*da6c28aaSamw 		    aclp++, numacls++) {
1664*da6c28aaSamw 			if (aclp->a_type & ACL_DEFAULT)
1665*da6c28aaSamw 				break;
1666*da6c28aaSamw 		}
1667*da6c28aaSamw 
1668*da6c28aaSamw 		/* Find where defaults start (if any) */
1669*da6c28aaSamw 		if (numacls < vsecattr->vsa_aclcnt) {
1670*da6c28aaSamw 			vsecattr->vsa_mask |= VSA_DFACL;
1671*da6c28aaSamw 			vsecattr->vsa_dfaclcnt = vsecattr->vsa_aclcnt - numacls;
1672*da6c28aaSamw 			vsecattr->vsa_dfaclentp = aclp;
1673*da6c28aaSamw 			vsecattr->vsa_aclcnt = numacls;
1674*da6c28aaSamw 		}
1675*da6c28aaSamw 
1676*da6c28aaSamw 		/* Adjust if they're all defaults */
1677*da6c28aaSamw 		if (vsecattr->vsa_aclcnt == 0) {
1678*da6c28aaSamw 			vsecattr->vsa_mask &= ~VSA_ACL;
1679*da6c28aaSamw 			vsecattr->vsa_aclentp = NULL;
1680*da6c28aaSamw 		}
1681*da6c28aaSamw 
1682*da6c28aaSamw 		/* Only directories can have defaults */
1683*da6c28aaSamw 		if (vsecattr->vsa_dfaclcnt &&
1684*da6c28aaSamw 		    (acl_info->acl_flags & ACL_IS_DIR)) {
1685*da6c28aaSamw 			error = ENOTDIR;
1686*da6c28aaSamw 		}
1687*da6c28aaSamw 
1688*da6c28aaSamw 		break;
1689*da6c28aaSamw 
1690*da6c28aaSamw 	case ACE_T:
1691*da6c28aaSamw 		if (acl_info->acl_cnt < 1 ||
1692*da6c28aaSamw 		    acl_info->acl_cnt > MAX_ACL_ENTRIES) {
1693*da6c28aaSamw 			error = EINVAL;
1694*da6c28aaSamw 			break;
1695*da6c28aaSamw 		}
1696*da6c28aaSamw 
1697*da6c28aaSamw 		vsecattr->vsa_mask = VSA_ACE | VSA_ACE_ACLFLAGS;
1698*da6c28aaSamw 		vsecattr->vsa_aclcnt = acl_info->acl_cnt;
1699*da6c28aaSamw 		vsecattr->vsa_aclflags = acl_info->acl_flags & ACL_FLAGS_ALL;
1700*da6c28aaSamw 		*aclbsize = vsecattr->vsa_aclcnt * sizeof (ace_t);
1701*da6c28aaSamw 		vsecattr->vsa_aclentsz = *aclbsize;
1702*da6c28aaSamw 		vsecattr->vsa_aclentp = kmem_alloc(*aclbsize, KM_SLEEP);
1703*da6c28aaSamw 		(void) memcpy(vsecattr->vsa_aclentp, acl_info->acl_aclp,
1704*da6c28aaSamw 		    *aclbsize);
1705*da6c28aaSamw 
1706*da6c28aaSamw 		break;
1707*da6c28aaSamw 
1708*da6c28aaSamw 	default:
1709*da6c28aaSamw 		error = EINVAL;
1710*da6c28aaSamw 	}
1711*da6c28aaSamw 
1712*da6c28aaSamw 	return (error);
1713*da6c28aaSamw }
1714*da6c28aaSamw 
1715*da6c28aaSamw /*
1716*da6c28aaSamw  * smb_vop_acl_read
1717*da6c28aaSamw  *
1718*da6c28aaSamw  * Reads the ACL of the specified file into 'aclp'.
1719*da6c28aaSamw  * acl_type is the type of ACL which the filesystem supports.
1720*da6c28aaSamw  *
1721*da6c28aaSamw  * Caller has to free the allocated memory for aclp by calling
1722*da6c28aaSamw  * acl_free().
1723*da6c28aaSamw  */
1724*da6c28aaSamw int
1725*da6c28aaSamw smb_vop_acl_read(vnode_t *vp, acl_t **aclp, int flags, acl_type_t acl_type,
1726*da6c28aaSamw     cred_t *cr, caller_context_t *ct)
1727*da6c28aaSamw {
1728*da6c28aaSamw 	int error;
1729*da6c28aaSamw 	vsecattr_t vsecattr;
1730*da6c28aaSamw 
1731*da6c28aaSamw 	ASSERT(vp);
1732*da6c28aaSamw 	ASSERT(aclp);
1733*da6c28aaSamw 
1734*da6c28aaSamw 	*aclp = NULL;
1735*da6c28aaSamw 	bzero(&vsecattr, sizeof (vsecattr_t));
1736*da6c28aaSamw 
1737*da6c28aaSamw 	switch (acl_type) {
1738*da6c28aaSamw 	case ACLENT_T:
1739*da6c28aaSamw 		vsecattr.vsa_mask = VSA_ACL | VSA_ACLCNT | VSA_DFACL |
1740*da6c28aaSamw 		    VSA_DFACLCNT;
1741*da6c28aaSamw 		break;
1742*da6c28aaSamw 
1743*da6c28aaSamw 	case ACE_T:
1744*da6c28aaSamw 		vsecattr.vsa_mask = VSA_ACE | VSA_ACECNT | VSA_ACE_ACLFLAGS;
1745*da6c28aaSamw 		break;
1746*da6c28aaSamw 
1747*da6c28aaSamw 	default:
1748*da6c28aaSamw 		return (EINVAL);
1749*da6c28aaSamw 	}
1750*da6c28aaSamw 
1751*da6c28aaSamw 	if (error = VOP_GETSECATTR(vp, &vsecattr, flags, cr, ct))
1752*da6c28aaSamw 		return (error);
1753*da6c28aaSamw 
1754*da6c28aaSamw 	*aclp = smb_vop_acl_from_vsa(&vsecattr, acl_type);
1755*da6c28aaSamw 	if (vp->v_type == VDIR)
1756*da6c28aaSamw 		(*aclp)->acl_flags |= ACL_IS_DIR;
1757*da6c28aaSamw 
1758*da6c28aaSamw 	return (0);
1759*da6c28aaSamw }
1760*da6c28aaSamw 
1761*da6c28aaSamw /*
1762*da6c28aaSamw  * smb_vop_acl_write
1763*da6c28aaSamw  *
1764*da6c28aaSamw  * Writes the given ACL in aclp for the specified file.
1765*da6c28aaSamw  */
1766*da6c28aaSamw int
1767*da6c28aaSamw smb_vop_acl_write(vnode_t *vp, acl_t *aclp, int flags, cred_t *cr,
1768*da6c28aaSamw     caller_context_t *ct)
1769*da6c28aaSamw {
1770*da6c28aaSamw 	int error;
1771*da6c28aaSamw 	vsecattr_t vsecattr;
1772*da6c28aaSamw 	int aclbsize;
1773*da6c28aaSamw 
1774*da6c28aaSamw 	ASSERT(vp);
1775*da6c28aaSamw 	ASSERT(aclp);
1776*da6c28aaSamw 
1777*da6c28aaSamw 	error = smb_vop_acl_to_vsa(aclp, &vsecattr, &aclbsize);
1778*da6c28aaSamw 
1779*da6c28aaSamw 	if (error == 0) {
1780*da6c28aaSamw 		(void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL);
1781*da6c28aaSamw 		error = VOP_SETSECATTR(vp, &vsecattr, flags, cr, ct);
1782*da6c28aaSamw 		VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
1783*da6c28aaSamw 	}
1784*da6c28aaSamw 
1785*da6c28aaSamw 	if (aclbsize && vsecattr.vsa_aclentp)
1786*da6c28aaSamw 		kmem_free(vsecattr.vsa_aclentp, aclbsize);
1787*da6c28aaSamw 
1788*da6c28aaSamw 	return (error);
1789*da6c28aaSamw }
1790*da6c28aaSamw 
1791*da6c28aaSamw /*
1792*da6c28aaSamw  * smb_vop_acl_type
1793*da6c28aaSamw  *
1794*da6c28aaSamw  * Determines the ACL type for the given vnode.
1795*da6c28aaSamw  * ACLENT_T is a Posix ACL and ACE_T is a ZFS ACL.
1796*da6c28aaSamw  */
1797*da6c28aaSamw acl_type_t
1798*da6c28aaSamw smb_vop_acl_type(vnode_t *vp)
1799*da6c28aaSamw {
1800*da6c28aaSamw 	int error;
1801*da6c28aaSamw 	ulong_t whichacl;
1802*da6c28aaSamw 
1803*da6c28aaSamw 	error = VOP_PATHCONF(vp, _PC_ACL_ENABLED, &whichacl, kcred, NULL);
1804*da6c28aaSamw 	if (error != 0) {
1805*da6c28aaSamw 		/*
1806*da6c28aaSamw 		 * If we got an error, then the filesystem
1807*da6c28aaSamw 		 * likely does not understand the _PC_ACL_ENABLED
1808*da6c28aaSamw 		 * pathconf.  In this case, we fall back to trying
1809*da6c28aaSamw 		 * POSIX-draft (aka UFS-style) ACLs.
1810*da6c28aaSamw 		 */
1811*da6c28aaSamw 		whichacl = _ACL_ACLENT_ENABLED;
1812*da6c28aaSamw 	}
1813*da6c28aaSamw 
1814*da6c28aaSamw 	if (!(whichacl & (_ACL_ACE_ENABLED | _ACL_ACLENT_ENABLED))) {
1815*da6c28aaSamw 		/*
1816*da6c28aaSamw 		 * If the file system supports neither ACE nor
1817*da6c28aaSamw 		 * ACLENT ACLs we will fall back to UFS-style ACLs
1818*da6c28aaSamw 		 * like we did above if there was an error upon
1819*da6c28aaSamw 		 * calling VOP_PATHCONF.
1820*da6c28aaSamw 		 *
1821*da6c28aaSamw 		 * ACE and ACLENT type ACLs are the only interfaces
1822*da6c28aaSamw 		 * supported thus far.  If any other bits are set on
1823*da6c28aaSamw 		 * 'whichacl' upon return from VOP_PATHCONF, we will
1824*da6c28aaSamw 		 * ignore them.
1825*da6c28aaSamw 		 */
1826*da6c28aaSamw 		whichacl = _ACL_ACLENT_ENABLED;
1827*da6c28aaSamw 	}
1828*da6c28aaSamw 
1829*da6c28aaSamw 	if (whichacl == _ACL_ACLENT_ENABLED)
1830*da6c28aaSamw 		return (ACLENT_T);
1831*da6c28aaSamw 
1832*da6c28aaSamw 	return (ACE_T);
1833*da6c28aaSamw }
1834*da6c28aaSamw 
1835*da6c28aaSamw static int zfs_perms[] = {
1836*da6c28aaSamw 	ACE_READ_DATA, ACE_WRITE_DATA, ACE_APPEND_DATA, ACE_READ_NAMED_ATTRS,
1837*da6c28aaSamw 	ACE_WRITE_NAMED_ATTRS, ACE_EXECUTE, ACE_DELETE_CHILD,
1838*da6c28aaSamw 	ACE_READ_ATTRIBUTES, ACE_WRITE_ATTRIBUTES, ACE_DELETE, ACE_READ_ACL,
1839*da6c28aaSamw 	ACE_WRITE_ACL, ACE_WRITE_OWNER, ACE_SYNCHRONIZE
1840*da6c28aaSamw };
1841*da6c28aaSamw 
1842*da6c28aaSamw static int unix_perms[] = { VREAD, VWRITE, VEXEC };
1843*da6c28aaSamw /*
1844*da6c28aaSamw  * smb_vop_eaccess
1845*da6c28aaSamw  *
1846*da6c28aaSamw  * Returns the effective permission of the given credential for the
1847*da6c28aaSamw  * specified object.
1848*da6c28aaSamw  *
1849*da6c28aaSamw  * This is just a workaround. We need VFS/FS support for this.
1850*da6c28aaSamw  */
1851*da6c28aaSamw void
1852*da6c28aaSamw smb_vop_eaccess(vnode_t *vp, int *mode, int flags, vnode_t *dir_vp, cred_t *cr)
1853*da6c28aaSamw {
1854*da6c28aaSamw 	int error, i;
1855*da6c28aaSamw 	int pnum;
1856*da6c28aaSamw 
1857*da6c28aaSamw 	*mode = 0;
1858*da6c28aaSamw 
1859*da6c28aaSamw 	if (flags == V_ACE_MASK) {
1860*da6c28aaSamw 		pnum = sizeof (zfs_perms) / sizeof (int);
1861*da6c28aaSamw 
1862*da6c28aaSamw 		for (i = 0; i < pnum; i++) {
1863*da6c28aaSamw 			error = smb_vop_access(vp, zfs_perms[i], flags,
1864*da6c28aaSamw 			    dir_vp, cr);
1865*da6c28aaSamw 			if (error == 0)
1866*da6c28aaSamw 				*mode |= zfs_perms[i];
1867*da6c28aaSamw 		}
1868*da6c28aaSamw 	} else {
1869*da6c28aaSamw 		pnum = sizeof (unix_perms) / sizeof (int);
1870*da6c28aaSamw 
1871*da6c28aaSamw 		for (i = 0; i < pnum; i++) {
1872*da6c28aaSamw 			error = smb_vop_access(vp, unix_perms[i], flags,
1873*da6c28aaSamw 			    dir_vp, cr);
1874*da6c28aaSamw 			if (error == 0)
1875*da6c28aaSamw 				*mode |= unix_perms[i];
1876*da6c28aaSamw 		}
1877*da6c28aaSamw 	}
1878*da6c28aaSamw }
1879