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