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