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