xref: /titanic_44/usr/src/uts/common/fs/smbsrv/smb_vops.c (revision 2d6b5ea734bb47d251c82670646fde46af15fd69)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <sys/uio.h>
29 #include <sys/statvfs.h>
30 #include <sys/vnode.h>
31 #include <sys/thread.h>
32 #include <sys/pathname.h>
33 #include <sys/cred.h>
34 #include <sys/extdirent.h>
35 #include <sys/nbmlock.h>
36 #include <sys/share.h>
37 #include <sys/fcntl.h>
38 #include <nfs/lm.h>
39 
40 #include <smbsrv/smb_vops.h>
41 #include <smbsrv/string.h>
42 
43 #include <smbsrv/smb_fsops.h>
44 #include <smbsrv/smb_kproto.h>
45 #include <smbsrv/smb_incl.h>
46 
47 static void smb_vop_setup_xvattr(smb_attr_t *smb_attr, xvattr_t *xvattr);
48 static void smb_sa_to_va_mask(uint_t sa_mask, uint_t *va_maskp);
49 static callb_cpr_t *smb_lock_frlock_callback(flk_cb_when_t, void *);
50 
51 extern sysid_t lm_alloc_sysidt();
52 
53 #define	SMB_AT_MAX	16
54 static uint_t smb_attrmap[SMB_AT_MAX] = {
55 	0,
56 	AT_TYPE,
57 	AT_MODE,
58 	AT_UID,
59 	AT_GID,
60 	AT_FSID,
61 	AT_NODEID,
62 	AT_NLINK,
63 	AT_SIZE,
64 	AT_ATIME,
65 	AT_MTIME,
66 	AT_CTIME,
67 	AT_RDEV,
68 	AT_BLKSIZE,
69 	AT_NBLOCKS,
70 	AT_SEQ
71 };
72 
73 static boolean_t	smb_vop_initialized = B_FALSE;
74 caller_context_t	smb_ct;
75 
76 /*
77  * smb_vop_init
78  *
79  * This function is not multi-thread safe. The caller must make sure only one
80  * thread makes the call.
81  */
82 int
83 smb_vop_init(void)
84 {
85 	if (smb_vop_initialized)
86 		return (0);
87 	/*
88 	 * The caller_context will be used primarily for range locking.
89 	 * Since the CIFS server is mapping its locks to POSIX locks,
90 	 * only one pid is used for operations originating from the
91 	 * CIFS server (to represent CIFS in the VOP_FRLOCK routines).
92 	 */
93 	smb_ct.cc_sysid = lm_alloc_sysidt();
94 	if (smb_ct.cc_sysid == LM_NOSYSID)
95 		return (ENOMEM);
96 
97 	smb_ct.cc_caller_id = fs_new_caller_id();
98 	smb_ct.cc_pid = IGN_PID;
99 	smb_ct.cc_flags = 0;
100 
101 	smb_vop_initialized = B_TRUE;
102 	return (0);
103 }
104 
105 /*
106  * smb_vop_fini
107  *
108  * This function is not multi-thread safe. The caller must make sure only one
109  * thread makes the call.
110  */
111 void
112 smb_vop_fini(void)
113 {
114 	if (!smb_vop_initialized)
115 		return;
116 
117 	lm_free_sysidt(smb_ct.cc_sysid);
118 	smb_ct.cc_pid = IGN_PID;
119 	smb_ct.cc_sysid = LM_NOSYSID;
120 	smb_vop_initialized = B_FALSE;
121 }
122 
123 /*
124  * The smb_ct will be used primarily for range locking.
125  * Since the CIFS server is mapping its locks to POSIX locks,
126  * only one pid is used for operations originating from the
127  * CIFS server (to represent CIFS in the VOP_FRLOCK routines).
128  */
129 
130 int
131 smb_vop_open(vnode_t **vpp, int mode, cred_t *cred)
132 {
133 	return (VOP_OPEN(vpp, mode, cred, &smb_ct));
134 }
135 
136 void
137 smb_vop_close(vnode_t *vp, int mode, cred_t *cred)
138 {
139 	(void) VOP_CLOSE(vp, mode, 1, (offset_t)0, cred, &smb_ct);
140 }
141 
142 int
143 smb_vop_other_opens(vnode_t *vp, int mode)
144 {
145 	return (((mode & FWRITE) && vn_has_other_opens(vp, V_WRITE)) ||
146 	    (((mode & FWRITE) == 0) && vn_is_opened(vp, V_WRITE)) ||
147 	    ((mode & FREAD) && vn_has_other_opens(vp, V_READ)) ||
148 	    (((mode & FREAD) == 0) && vn_is_opened(vp, V_READ)) ||
149 	    vn_is_mapped(vp, V_RDORWR));
150 }
151 
152 /*
153  * The smb_vop_* functions have minimal knowledge of CIFS semantics and
154  * serve as an interface to the VFS layer.
155  *
156  * Only smb_fsop_* layer functions should call smb_vop_* layer functions.
157  * (Higher-level CIFS service code should never skip the smb_fsop_* layer
158  * to call smb_vop_* layer functions directly.)
159  */
160 
161 /*
162  * XXX - Extended attributes support in the file system assumed.
163  * This is needed for full NT Streams functionality.
164  */
165 
166 int
167 smb_vop_read(vnode_t *vp, uio_t *uiop, cred_t *cr)
168 {
169 	int error;
170 
171 	(void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, &smb_ct);
172 	error = VOP_READ(vp, uiop, 0, cr, &smb_ct);
173 	VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, &smb_ct);
174 	return (error);
175 }
176 
177 int
178 smb_vop_write(vnode_t *vp, uio_t *uiop, int ioflag, uint32_t *lcount,
179     cred_t *cr)
180 {
181 	int error;
182 
183 	*lcount = uiop->uio_resid;
184 
185 	uiop->uio_llimit = MAXOFFSET_T;
186 
187 	(void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, &smb_ct);
188 	error = VOP_WRITE(vp, uiop, ioflag, cr, &smb_ct);
189 	VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, &smb_ct);
190 
191 	*lcount -= uiop->uio_resid;
192 
193 	return (error);
194 }
195 
196 /*
197  * smb_vop_getattr()
198  *
199  * smb_fsop_getattr()/smb_vop_getattr() should always be called from the CIFS
200  * service (instead of calling VOP_GETATTR directly) to retrieve attributes
201  * due to special processing needed for streams files.
202  *
203  * All attributes are retrieved.
204  *
205  * When vp denotes a named stream, then unnamed_vp should be passed in (denoting
206  * the corresponding unnamed stream).
207  * A named stream's attributes (as far as CIFS is concerned) are those of the
208  * unnamed stream (minus the size attribute, and the type), plus  the size of
209  * the named stream, and a type value of VREG.
210  * Although the file system may store other attributes with the named stream,
211  * these should not be used by CIFS for any purpose.
212  *
213  * File systems without VFSFT_XVATTR do not support DOS attributes or create
214  * time (crtime). In this case the mtime is used as the crtime.
215  * Likewise if VOP_GETATTR doesn't return any system attributes the dosattr
216  * is 0 and the mtime is used as the crtime.
217  */
218 int
219 smb_vop_getattr(vnode_t *vp, vnode_t *unnamed_vp, smb_attr_t *ret_attr,
220     int flags, cred_t *cr)
221 {
222 	int error;
223 	vnode_t *use_vp;
224 	smb_attr_t tmp_attr;
225 	xvattr_t tmp_xvattr;
226 	xoptattr_t *xoap = NULL;
227 
228 	if (unnamed_vp)
229 		use_vp = unnamed_vp;
230 	else
231 		use_vp = vp;
232 
233 	if (vfs_has_feature(use_vp->v_vfsp, VFSFT_XVATTR)) {
234 		xva_init(&tmp_xvattr);
235 		xoap = xva_getxoptattr(&tmp_xvattr);
236 		ASSERT(xoap);
237 
238 		smb_sa_to_va_mask(ret_attr->sa_mask,
239 		    &tmp_xvattr.xva_vattr.va_mask);
240 
241 		XVA_SET_REQ(&tmp_xvattr, XAT_READONLY);
242 		XVA_SET_REQ(&tmp_xvattr, XAT_HIDDEN);
243 		XVA_SET_REQ(&tmp_xvattr, XAT_SYSTEM);
244 		XVA_SET_REQ(&tmp_xvattr, XAT_ARCHIVE);
245 		XVA_SET_REQ(&tmp_xvattr, XAT_CREATETIME);
246 
247 		error = VOP_GETATTR(use_vp, &tmp_xvattr.xva_vattr, flags,
248 		    cr, &smb_ct);
249 		if (error != 0)
250 			return (error);
251 
252 		ret_attr->sa_vattr = tmp_xvattr.xva_vattr;
253 		ret_attr->sa_dosattr = 0;
254 
255 		if (tmp_xvattr.xva_vattr.va_mask & AT_XVATTR) {
256 			xoap = xva_getxoptattr(&tmp_xvattr);
257 			ASSERT(xoap);
258 
259 			if ((XVA_ISSET_RTN(&tmp_xvattr, XAT_READONLY)) &&
260 			    (xoap->xoa_readonly)) {
261 				ret_attr->sa_dosattr |= FILE_ATTRIBUTE_READONLY;
262 			}
263 
264 			if ((XVA_ISSET_RTN(&tmp_xvattr, XAT_HIDDEN)) &&
265 			    (xoap->xoa_hidden)) {
266 				ret_attr->sa_dosattr |= FILE_ATTRIBUTE_HIDDEN;
267 			}
268 
269 			if ((XVA_ISSET_RTN(&tmp_xvattr, XAT_SYSTEM)) &&
270 			    (xoap->xoa_system)) {
271 				ret_attr->sa_dosattr |= FILE_ATTRIBUTE_SYSTEM;
272 			}
273 
274 			if ((XVA_ISSET_RTN(&tmp_xvattr, XAT_ARCHIVE)) &&
275 			    (xoap->xoa_archive)) {
276 				ret_attr->sa_dosattr |= FILE_ATTRIBUTE_ARCHIVE;
277 			}
278 
279 			ret_attr->sa_crtime = xoap->xoa_createtime;
280 		} else {
281 			ret_attr->sa_crtime = ret_attr->sa_vattr.va_mtime;
282 		}
283 	} else {
284 		/*
285 		 * Support for file systems without VFSFT_XVATTR
286 		 */
287 		smb_sa_to_va_mask(ret_attr->sa_mask,
288 		    &ret_attr->sa_vattr.va_mask);
289 
290 		error = VOP_GETATTR(use_vp, &ret_attr->sa_vattr,
291 		    flags, cr, &smb_ct);
292 		if (error != 0)
293 			return (error);
294 
295 		ret_attr->sa_dosattr = 0;
296 		ret_attr->sa_crtime = ret_attr->sa_vattr.va_mtime;
297 	}
298 
299 	if (unnamed_vp) {
300 		ret_attr->sa_vattr.va_type = VREG;
301 
302 		if (ret_attr->sa_mask & SMB_AT_SIZE) {
303 			tmp_attr.sa_vattr.va_mask = AT_SIZE;
304 
305 			error = VOP_GETATTR(vp, &tmp_attr.sa_vattr,
306 			    flags, cr, &smb_ct);
307 			if (error != 0)
308 				return (error);
309 
310 			ret_attr->sa_vattr.va_size = tmp_attr.sa_vattr.va_size;
311 		}
312 	}
313 
314 	if (ret_attr->sa_vattr.va_type == VDIR)
315 		ret_attr->sa_dosattr |= FILE_ATTRIBUTE_DIRECTORY;
316 
317 	return (error);
318 }
319 
320 /*
321  * smb_vop_setattr()
322  *
323  * smb_fsop_setattr()/smb_vop_setattr() should always be used instead of
324  * VOP_SETATTR() when calling from the CIFS service, due to special processing
325  * for streams files.
326  *
327  * Streams have a size but otherwise do not have separate attributes from
328  * the (unnamed stream) file, i.e., the security and ownership of the file
329  * applies to the stream.  In contrast, extended attribute files, which are
330  * used to implement streams, are independent objects with their own
331  * attributes.
332  *
333  * For compatibility with streams, we set the size on the extended attribute
334  * file and apply other attributes to the (unnamed stream) file.  The one
335  * exception is that the UID and GID can be set on the stream by passing a
336  * NULL unnamed_vp, which allows callers to synchronize stream ownership
337  * with the (unnamed stream) file.
338  */
339 
340 int
341 smb_vop_setattr(vnode_t *vp, vnode_t *unnamed_vp, smb_attr_t *set_attr,
342     int flags, cred_t *cr)
343 {
344 	int error = 0;
345 	int at_size = 0;
346 	vnode_t *use_vp;
347 	xvattr_t xvattr;
348 	vattr_t *vap;
349 
350 	if (unnamed_vp) {
351 		use_vp = unnamed_vp;
352 		if (set_attr->sa_mask & SMB_AT_SIZE) {
353 			at_size = 1;
354 			set_attr->sa_mask &= ~SMB_AT_SIZE;
355 		}
356 	} else {
357 		use_vp = vp;
358 	}
359 
360 	/*
361 	 * The caller should not be setting sa_vattr.va_mask,
362 	 * but rather sa_mask.
363 	 */
364 
365 	set_attr->sa_vattr.va_mask = 0;
366 
367 	if (vfs_has_feature(use_vp->v_vfsp, VFSFT_XVATTR)) {
368 		smb_vop_setup_xvattr(set_attr, &xvattr);
369 		vap = &xvattr.xva_vattr;
370 	} else {
371 		smb_sa_to_va_mask(set_attr->sa_mask,
372 		    &set_attr->sa_vattr.va_mask);
373 		vap = &set_attr->sa_vattr;
374 	}
375 
376 	if ((error = VOP_SETATTR(use_vp, vap, flags, cr, &smb_ct)) != 0)
377 		return (error);
378 
379 	if (at_size) {
380 		set_attr->sa_vattr.va_mask = AT_SIZE;
381 		error = VOP_SETATTR(vp, &set_attr->sa_vattr, flags, cr,
382 		    &smb_ct);
383 	}
384 
385 	return (error);
386 }
387 
388 /*
389  * smb_vop_access
390  *
391  * This is a wrapper round VOP_ACCESS. VOP_ACCESS checks the given mode
392  * against file's ACL or Unix permissions. CIFS on the other hand needs to
393  * know if the requested operation can succeed for the given object, this
394  * requires more checks in case of DELETE bit since permissions on the parent
395  * directory are important as well. Based on Windows rules if parent's ACL
396  * grant FILE_DELETE_CHILD a file can be delete regardless of the file's
397  * permissions.
398  */
399 int
400 smb_vop_access(vnode_t *vp, int mode, int flags, vnode_t *dir_vp, cred_t *cr)
401 {
402 	int error = 0;
403 
404 	if (mode == 0)
405 		return (0);
406 
407 	if ((flags == V_ACE_MASK) && (mode & ACE_DELETE)) {
408 		if (dir_vp) {
409 			error = VOP_ACCESS(dir_vp, ACE_DELETE_CHILD, flags,
410 			    cr, NULL);
411 
412 			if (error == 0)
413 				mode &= ~ACE_DELETE;
414 		}
415 	}
416 
417 	if (mode) {
418 		error = VOP_ACCESS(vp, mode, flags, cr, NULL);
419 	}
420 
421 	return (error);
422 }
423 
424 /*
425  * smb_vop_lookup
426  *
427  * dvp:		directory vnode (in)
428  * name:	name of file to be looked up (in)
429  * vpp:		looked-up vnode (out)
430  * od_name:	on-disk name of file (out).
431  *		This parameter is optional.  If a pointer is passed in, it
432  * 		must be allocated with MAXNAMELEN bytes
433  * rootvp:	vnode of the tree root (in)
434  *		This parameter is always passed in non-NULL except at the time
435  *		of share set up.
436  */
437 
438 int
439 smb_vop_lookup(
440     vnode_t		*dvp,
441     char		*name,
442     vnode_t		**vpp,
443     char		*od_name,
444     int			flags,
445     vnode_t		*rootvp,
446     cred_t		*cr)
447 {
448 	int error = 0;
449 	int option_flags = 0;
450 	pathname_t rpn;
451 
452 	if (*name == '\0')
453 		return (EINVAL);
454 
455 	ASSERT(vpp);
456 	*vpp = NULL;
457 
458 	if ((name[0] == '.') && (name[1] == '.') && (name[2] == 0)) {
459 		if (rootvp && (dvp == rootvp)) {
460 			VN_HOLD(dvp);
461 			*vpp = dvp;
462 			return (0);
463 		}
464 
465 		if (dvp->v_flag & VROOT) {
466 			vfs_t *vfsp;
467 			vnode_t *cvp = dvp;
468 
469 			/*
470 			 * Set dvp and check for races with forced unmount
471 			 * (see lookuppnvp())
472 			 */
473 
474 			vfsp = cvp->v_vfsp;
475 			vfs_rlock_wait(vfsp);
476 			if (((dvp = cvp->v_vfsp->vfs_vnodecovered) == NULL) ||
477 			    (cvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)) {
478 				vfs_unlock(vfsp);
479 				return (EIO);
480 			}
481 			vfs_unlock(vfsp);
482 		}
483 	}
484 
485 
486 
487 	if (flags & SMB_IGNORE_CASE)
488 		option_flags = FIGNORECASE;
489 
490 	pn_alloc(&rpn);
491 
492 	error = VOP_LOOKUP(dvp, name, vpp, NULL, option_flags, NULL, cr,
493 	    &smb_ct, NULL, &rpn);
494 
495 	if ((error == 0) && od_name) {
496 		bzero(od_name, MAXNAMELEN);
497 		if (option_flags == FIGNORECASE)
498 			(void) strlcpy(od_name, rpn.pn_buf, MAXNAMELEN);
499 		else
500 			(void) strlcpy(od_name, name, MAXNAMELEN);
501 	}
502 
503 	pn_free(&rpn);
504 	return (error);
505 }
506 
507 int
508 smb_vop_create(vnode_t *dvp, char *name, smb_attr_t *attr, vnode_t **vpp,
509     int flags, cred_t *cr, vsecattr_t *vsap)
510 {
511 	int error;
512 	int option_flags = 0;
513 	xvattr_t xvattr;
514 	vattr_t *vap;
515 
516 	if (flags & SMB_IGNORE_CASE)
517 		option_flags = FIGNORECASE;
518 
519 	attr->sa_vattr.va_mask = 0;
520 
521 	if (vfs_has_feature(dvp->v_vfsp, VFSFT_XVATTR)) {
522 		smb_vop_setup_xvattr(attr, &xvattr);
523 		vap = &xvattr.xva_vattr;
524 	} else {
525 		smb_sa_to_va_mask(attr->sa_mask, &attr->sa_vattr.va_mask);
526 		vap = &attr->sa_vattr;
527 	}
528 
529 	error = VOP_CREATE(dvp, name, vap, EXCL, attr->sa_vattr.va_mode,
530 	    vpp, cr, option_flags, &smb_ct, vsap);
531 
532 	return (error);
533 }
534 
535 int
536 smb_vop_remove(vnode_t *dvp, char *name, int flags, cred_t *cr)
537 {
538 	int error;
539 	int option_flags = 0;
540 
541 	if (flags & SMB_IGNORE_CASE)
542 		option_flags = FIGNORECASE;
543 
544 	error = VOP_REMOVE(dvp, name, cr, &smb_ct, option_flags);
545 
546 	return (error);
547 }
548 
549 /*
550  * smb_vop_rename()
551  *
552  * The rename is for files in the same tree (identical TID) only.
553  */
554 
555 int
556 smb_vop_rename(vnode_t *from_dvp, char *from_name, vnode_t *to_dvp,
557     char *to_name, int flags, cred_t *cr)
558 {
559 	int error;
560 	int option_flags = 0;
561 
562 
563 	if (flags & SMB_IGNORE_CASE)
564 		option_flags = FIGNORECASE;
565 
566 	error = VOP_RENAME(from_dvp, from_name, to_dvp, to_name, cr,
567 	    &smb_ct, option_flags);
568 
569 	return (error);
570 }
571 
572 int
573 smb_vop_mkdir(vnode_t *dvp, char *name, smb_attr_t *attr, vnode_t **vpp,
574     int flags, cred_t *cr, vsecattr_t *vsap)
575 {
576 	int error;
577 	int option_flags = 0;
578 	xvattr_t xvattr;
579 	vattr_t *vap;
580 
581 	if (flags & SMB_IGNORE_CASE)
582 		option_flags = FIGNORECASE;
583 
584 	attr->sa_vattr.va_mask = 0;
585 
586 	if (vfs_has_feature(dvp->v_vfsp, VFSFT_XVATTR)) {
587 		smb_vop_setup_xvattr(attr, &xvattr);
588 		vap = &xvattr.xva_vattr;
589 	} else {
590 		smb_sa_to_va_mask(attr->sa_mask, &attr->sa_vattr.va_mask);
591 		vap = &attr->sa_vattr;
592 	}
593 
594 	error = VOP_MKDIR(dvp, name, vap, vpp, cr, &smb_ct,
595 	    option_flags, vsap);
596 
597 	return (error);
598 }
599 
600 /*
601  * smb_vop_rmdir()
602  *
603  * Only simple rmdir supported, consistent with NT semantics
604  * (can only remove an empty directory).
605  *
606  */
607 
608 int
609 smb_vop_rmdir(vnode_t *dvp, char *name, int flags, cred_t *cr)
610 {
611 	int error;
612 	int option_flags = 0;
613 
614 	if (flags & SMB_IGNORE_CASE)
615 		option_flags = FIGNORECASE;
616 
617 	/*
618 	 * Comments adapted from rfs_rmdir().
619 	 *
620 	 * VOP_RMDIR now takes a new third argument (the current
621 	 * directory of the process).  That's because rmdir
622 	 * wants to return EINVAL if one tries to remove ".".
623 	 * Of course, SMB servers do not know what their
624 	 * clients' current directories are.  We fake it by
625 	 * supplying a vnode known to exist and illegal to
626 	 * remove.
627 	 */
628 
629 	error = VOP_RMDIR(dvp, name, rootdir, cr, &smb_ct, option_flags);
630 	return (error);
631 }
632 
633 int
634 smb_vop_commit(vnode_t *vp, cred_t *cr)
635 {
636 	return (VOP_FSYNC(vp, 1, cr, &smb_ct));
637 }
638 
639 static void
640 smb_vop_setup_xvattr(smb_attr_t *smb_attr, xvattr_t *xvattr)
641 {
642 	xoptattr_t *xoap = NULL;
643 	uint_t xva_mask;
644 
645 	/*
646 	 * Initialize xvattr, including bzero
647 	 */
648 	xva_init(xvattr);
649 	xoap = xva_getxoptattr(xvattr);
650 
651 	ASSERT(xoap);
652 
653 	/*
654 	 * Copy caller-specified classic attributes to xvattr.
655 	 * First save xvattr's mask (set in xva_init()), which
656 	 * contains AT_XVATTR.  This is |'d in later if needed.
657 	 */
658 
659 	xva_mask = xvattr->xva_vattr.va_mask;
660 	xvattr->xva_vattr = smb_attr->sa_vattr;
661 
662 	smb_sa_to_va_mask(smb_attr->sa_mask, &xvattr->xva_vattr.va_mask);
663 
664 	/*
665 	 * Do not set ctime (only the file system can do it)
666 	 */
667 
668 	xvattr->xva_vattr.va_mask &= ~AT_CTIME;
669 
670 	if (smb_attr->sa_mask & SMB_AT_DOSATTR) {
671 
672 		/*
673 		 * "|" in the original xva_mask, which contains
674 		 * AT_XVATTR
675 		 */
676 
677 		xvattr->xva_vattr.va_mask |= xva_mask;
678 
679 		XVA_SET_REQ(xvattr, XAT_ARCHIVE);
680 		XVA_SET_REQ(xvattr, XAT_SYSTEM);
681 		XVA_SET_REQ(xvattr, XAT_READONLY);
682 		XVA_SET_REQ(xvattr, XAT_HIDDEN);
683 
684 		/*
685 		 * smb_attr->sa_dosattr: If a given bit is not set,
686 		 * that indicates that the corresponding field needs
687 		 * to be updated with a "0" value.  This is done
688 		 * implicitly as the xoap->xoa_* fields were bzero'd.
689 		 */
690 
691 		if (smb_attr->sa_dosattr & FILE_ATTRIBUTE_ARCHIVE)
692 			xoap->xoa_archive = 1;
693 
694 		if (smb_attr->sa_dosattr & FILE_ATTRIBUTE_SYSTEM)
695 			xoap->xoa_system = 1;
696 
697 		if (smb_attr->sa_dosattr & FILE_ATTRIBUTE_READONLY)
698 			xoap->xoa_readonly = 1;
699 
700 		if (smb_attr->sa_dosattr & FILE_ATTRIBUTE_HIDDEN)
701 			xoap->xoa_hidden = 1;
702 	}
703 
704 	if (smb_attr->sa_mask & SMB_AT_CRTIME) {
705 		/*
706 		 * "|" in the original xva_mask, which contains
707 		 * AT_XVATTR
708 		 */
709 
710 		xvattr->xva_vattr.va_mask |= xva_mask;
711 		XVA_SET_REQ(xvattr, XAT_CREATETIME);
712 		xoap->xoa_createtime = smb_attr->sa_crtime;
713 	}
714 }
715 
716 /*
717  * smb_vop_readdir()
718  *
719  * Collects an SMB_MINLEN_RDDIR_BUF "page" of directory entries.
720  * The directory entries are returned in an fs-independent format by the
721  * underlying file system.  That is, the "page" of information returned is
722  * not literally stored on-disk in the format returned.
723  * If the file system supports extended directory entries (has features
724  * VFSFT_DIRENTFLAGS), set V_RDDIR_ENTFLAGS to cause the buffer to be
725  * filled with edirent_t structures, instead of dirent64_t structures.
726  *
727  * Some file systems can have directories larger than SMB_MAXDIRSIZE.
728  * After VOP_READDIR, if offset is larger than SMB_MAXDIRSIZE treat as EOF.
729  */
730 int
731 smb_vop_readdir(vnode_t *vp, uint32_t offset,
732     void *buf, int *count, int *eof, cred_t *cr)
733 {
734 	int error = 0;
735 	int rdirent_flags = 0;
736 	int rdirent_size;
737 	struct uio auio;
738 	struct iovec aiov;
739 
740 	if (vp->v_type != VDIR)
741 		return (ENOTDIR);
742 
743 	if (vfs_has_feature(vp->v_vfsp, VFSFT_DIRENTFLAGS)) {
744 		rdirent_flags = V_RDDIR_ENTFLAGS;
745 		rdirent_size = sizeof (edirent_t);
746 	} else {
747 		rdirent_size = sizeof (dirent64_t);
748 	}
749 
750 	if (*count < rdirent_size)
751 		return (EINVAL);
752 
753 	aiov.iov_base = buf;
754 	aiov.iov_len = *count;
755 	auio.uio_iov = &aiov;
756 	auio.uio_iovcnt = 1;
757 	auio.uio_loffset = (uint64_t)offset;
758 	auio.uio_segflg = UIO_SYSSPACE;
759 	auio.uio_resid = *count;
760 	auio.uio_fmode = 0;
761 
762 	(void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, &smb_ct);
763 	error = VOP_READDIR(vp, &auio, cr, eof, &smb_ct, rdirent_flags);
764 	VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, &smb_ct);
765 
766 	if (auio.uio_loffset > SMB_MAXDIRSIZE)
767 		*eof = 1;
768 
769 	if (error == 0)
770 		*count = *count - auio.uio_resid;
771 
772 	return (error);
773 }
774 
775 /*
776  * smb_sa_to_va_mask
777  *
778  * Set va_mask by running through the SMB_AT_* #define's and
779  * setting those bits that correspond to the SMB_AT_* bits
780  * set in sa_mask.
781  */
782 
783 void
784 smb_sa_to_va_mask(uint_t sa_mask, uint_t *va_maskp)
785 {
786 	int i;
787 	uint_t smask;
788 
789 	smask = (sa_mask);
790 	for (i = SMB_AT_TYPE; (i < SMB_AT_MAX) && (smask != 0); ++i) {
791 		if (smask & 1)
792 			*(va_maskp) |= smb_attrmap[i];
793 
794 		smask >>= 1;
795 	}
796 }
797 
798 /*
799  * smb_vop_stream_lookup()
800  *
801  * The name returned in od_name is the on-disk name of the stream with the
802  * SMB_STREAM_PREFIX stripped off.  od_name should be allocated to MAXNAMELEN
803  * by the caller.
804  */
805 
806 int
807 smb_vop_stream_lookup(
808     vnode_t		*fvp,
809     char		*stream_name,
810     vnode_t		**vpp,
811     char		*od_name,
812     vnode_t		**xattrdirvpp,
813     int			flags,
814     vnode_t		*rootvp,
815     cred_t		*cr)
816 {
817 	char *solaris_stream_name;
818 	char *name;
819 	int error;
820 
821 	if ((error = smb_vop_lookup_xattrdir(fvp, xattrdirvpp,
822 	    LOOKUP_XATTR | CREATE_XATTR_DIR, cr)) != 0)
823 		return (error);
824 
825 	/*
826 	 * Prepend SMB_STREAM_PREFIX to stream name
827 	 */
828 
829 	solaris_stream_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
830 	(void) sprintf(solaris_stream_name, "%s%s", SMB_STREAM_PREFIX,
831 	    stream_name);
832 
833 	/*
834 	 * "name" will hold the on-disk name returned from smb_vop_lookup
835 	 * for the stream, including the SMB_STREAM_PREFIX.
836 	 */
837 
838 	name = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
839 
840 	if ((error = smb_vop_lookup(*xattrdirvpp, solaris_stream_name, vpp,
841 	    name, flags, rootvp, cr)) != 0) {
842 		VN_RELE(*xattrdirvpp);
843 	} else {
844 		(void) strlcpy(od_name, &(name[SMB_STREAM_PREFIX_LEN]),
845 		    MAXNAMELEN);
846 	}
847 
848 	kmem_free(solaris_stream_name, MAXNAMELEN);
849 	kmem_free(name, MAXNAMELEN);
850 
851 	return (error);
852 }
853 
854 int
855 smb_vop_stream_create(vnode_t *fvp, char *stream_name, smb_attr_t *attr,
856     vnode_t **vpp, vnode_t **xattrdirvpp, int flags, cred_t *cr)
857 {
858 	char *solaris_stream_name;
859 	int error;
860 
861 	if ((error = smb_vop_lookup_xattrdir(fvp, xattrdirvpp,
862 	    LOOKUP_XATTR | CREATE_XATTR_DIR, cr)) != 0)
863 		return (error);
864 
865 	/*
866 	 * Prepend SMB_STREAM_PREFIX to stream name
867 	 */
868 
869 	solaris_stream_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
870 	(void) sprintf(solaris_stream_name, "%s%s", SMB_STREAM_PREFIX,
871 	    stream_name);
872 
873 	if ((error = smb_vop_create(*xattrdirvpp, solaris_stream_name, attr,
874 	    vpp, flags, cr, NULL)) != 0)
875 		VN_RELE(*xattrdirvpp);
876 
877 	kmem_free(solaris_stream_name, MAXNAMELEN);
878 
879 	return (error);
880 }
881 
882 int
883 smb_vop_stream_remove(vnode_t *vp, char *stream_name, int flags, cred_t *cr)
884 {
885 	char *solaris_stream_name;
886 	vnode_t *xattrdirvp;
887 	int error;
888 
889 	error = smb_vop_lookup_xattrdir(vp, &xattrdirvp, LOOKUP_XATTR, cr);
890 	if (error != 0)
891 		return (error);
892 
893 	/*
894 	 * Prepend SMB_STREAM_PREFIX to stream name
895 	 */
896 
897 	solaris_stream_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
898 	(void) sprintf(solaris_stream_name, "%s%s", SMB_STREAM_PREFIX,
899 	    stream_name);
900 
901 	/* XXX might have to use kcred */
902 	error = smb_vop_remove(xattrdirvp, solaris_stream_name, flags, cr);
903 
904 	kmem_free(solaris_stream_name, MAXNAMELEN);
905 
906 	return (error);
907 }
908 
909 int
910 smb_vop_lookup_xattrdir(vnode_t *fvp, vnode_t **xattrdirvpp, int flags,
911     cred_t *cr)
912 {
913 	int error;
914 
915 	error = VOP_LOOKUP(fvp, "", xattrdirvpp, NULL, flags, NULL, cr,
916 	    &smb_ct, NULL, NULL);
917 	return (error);
918 }
919 
920 /*
921  * smb_vop_traverse_check()
922  *
923  * This function checks to see if the passed-in vnode has a file system
924  * mounted on it.  If it does, the mount point is "traversed" and the
925  * vnode for the root of the file system is returned.
926  */
927 
928 int
929 smb_vop_traverse_check(vnode_t **vpp)
930 {
931 	int error;
932 
933 	if (vn_mountedvfs(*vpp) == 0)
934 		return (0);
935 
936 	/*
937 	 * traverse() may return a different held vnode, even in the error case.
938 	 * If it returns a different vnode, it will have released the original.
939 	 */
940 
941 	error = traverse(vpp);
942 
943 	return (error);
944 }
945 
946 int /*ARGSUSED*/
947 smb_vop_statfs(vnode_t *vp, struct statvfs64 *statp, cred_t *cr)
948 {
949 	int error;
950 
951 	error = VFS_STATVFS(vp->v_vfsp, statp);
952 
953 	return (error);
954 }
955 
956 /*
957  * smb_vop_acl_read
958  *
959  * Reads the ACL of the specified file into 'aclp'.
960  * acl_type is the type of ACL which the filesystem supports.
961  *
962  * Caller has to free the allocated memory for aclp by calling
963  * acl_free().
964  */
965 int
966 smb_vop_acl_read(vnode_t *vp, acl_t **aclp, int flags, acl_type_t acl_type,
967     cred_t *cr)
968 {
969 	int error;
970 	vsecattr_t vsecattr;
971 
972 	ASSERT(vp);
973 	ASSERT(aclp);
974 
975 	*aclp = NULL;
976 	bzero(&vsecattr, sizeof (vsecattr_t));
977 
978 	switch (acl_type) {
979 	case ACLENT_T:
980 		vsecattr.vsa_mask = VSA_ACL | VSA_ACLCNT | VSA_DFACL |
981 		    VSA_DFACLCNT;
982 		break;
983 
984 	case ACE_T:
985 		vsecattr.vsa_mask = VSA_ACE | VSA_ACECNT | VSA_ACE_ACLFLAGS;
986 		break;
987 
988 	default:
989 		return (EINVAL);
990 	}
991 
992 	if (error = VOP_GETSECATTR(vp, &vsecattr, flags, cr, &smb_ct))
993 		return (error);
994 
995 	*aclp = smb_fsacl_from_vsa(&vsecattr, acl_type);
996 	if (vp->v_type == VDIR)
997 		(*aclp)->acl_flags |= ACL_IS_DIR;
998 
999 	return (0);
1000 }
1001 
1002 /*
1003  * smb_vop_acl_write
1004  *
1005  * Writes the given ACL in aclp for the specified file.
1006  */
1007 int
1008 smb_vop_acl_write(vnode_t *vp, acl_t *aclp, int flags, cred_t *cr)
1009 {
1010 	int error;
1011 	vsecattr_t vsecattr;
1012 	int aclbsize;
1013 
1014 	ASSERT(vp);
1015 	ASSERT(aclp);
1016 
1017 	error = smb_fsacl_to_vsa(aclp, &vsecattr, &aclbsize);
1018 
1019 	if (error == 0) {
1020 		(void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, &smb_ct);
1021 		error = VOP_SETSECATTR(vp, &vsecattr, flags, cr, &smb_ct);
1022 		VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, &smb_ct);
1023 	}
1024 
1025 	if (aclbsize && vsecattr.vsa_aclentp)
1026 		kmem_free(vsecattr.vsa_aclentp, aclbsize);
1027 
1028 	return (error);
1029 }
1030 
1031 /*
1032  * smb_vop_acl_type
1033  *
1034  * Determines the ACL type for the given vnode.
1035  * ACLENT_T is a Posix ACL and ACE_T is a ZFS ACL.
1036  */
1037 acl_type_t
1038 smb_vop_acl_type(vnode_t *vp)
1039 {
1040 	int error;
1041 	ulong_t whichacl;
1042 
1043 	error = VOP_PATHCONF(vp, _PC_ACL_ENABLED, &whichacl, kcred, NULL);
1044 	if (error != 0) {
1045 		/*
1046 		 * If we got an error, then the filesystem
1047 		 * likely does not understand the _PC_ACL_ENABLED
1048 		 * pathconf.  In this case, we fall back to trying
1049 		 * POSIX-draft (aka UFS-style) ACLs.
1050 		 */
1051 		whichacl = _ACL_ACLENT_ENABLED;
1052 	}
1053 
1054 	if (!(whichacl & (_ACL_ACE_ENABLED | _ACL_ACLENT_ENABLED))) {
1055 		/*
1056 		 * If the file system supports neither ACE nor
1057 		 * ACLENT ACLs we will fall back to UFS-style ACLs
1058 		 * like we did above if there was an error upon
1059 		 * calling VOP_PATHCONF.
1060 		 *
1061 		 * ACE and ACLENT type ACLs are the only interfaces
1062 		 * supported thus far.  If any other bits are set on
1063 		 * 'whichacl' upon return from VOP_PATHCONF, we will
1064 		 * ignore them.
1065 		 */
1066 		whichacl = _ACL_ACLENT_ENABLED;
1067 	}
1068 
1069 	if (whichacl == _ACL_ACLENT_ENABLED)
1070 		return (ACLENT_T);
1071 
1072 	return (ACE_T);
1073 }
1074 
1075 static int zfs_perms[] = {
1076 	ACE_READ_DATA, ACE_WRITE_DATA, ACE_APPEND_DATA, ACE_READ_NAMED_ATTRS,
1077 	ACE_WRITE_NAMED_ATTRS, ACE_EXECUTE, ACE_DELETE_CHILD,
1078 	ACE_READ_ATTRIBUTES, ACE_WRITE_ATTRIBUTES, ACE_DELETE, ACE_READ_ACL,
1079 	ACE_WRITE_ACL, ACE_WRITE_OWNER, ACE_SYNCHRONIZE
1080 };
1081 
1082 static int unix_perms[] = { VREAD, VWRITE, VEXEC };
1083 /*
1084  * smb_vop_eaccess
1085  *
1086  * Returns the effective permission of the given credential for the
1087  * specified object.
1088  *
1089  * This is just a workaround. We need VFS/FS support for this.
1090  */
1091 void
1092 smb_vop_eaccess(vnode_t *vp, int *mode, int flags, vnode_t *dir_vp, cred_t *cr)
1093 {
1094 	int error, i;
1095 	int pnum;
1096 
1097 	*mode = 0;
1098 
1099 	if (flags == V_ACE_MASK) {
1100 		pnum = sizeof (zfs_perms) / sizeof (int);
1101 
1102 		for (i = 0; i < pnum; i++) {
1103 			error = smb_vop_access(vp, zfs_perms[i], flags,
1104 			    dir_vp, cr);
1105 			if (error == 0)
1106 				*mode |= zfs_perms[i];
1107 		}
1108 	} else {
1109 		pnum = sizeof (unix_perms) / sizeof (int);
1110 
1111 		for (i = 0; i < pnum; i++) {
1112 			error = smb_vop_access(vp, unix_perms[i], flags,
1113 			    dir_vp, cr);
1114 			if (error == 0)
1115 				*mode |= unix_perms[i];
1116 		}
1117 	}
1118 }
1119 
1120 /*
1121  * smb_vop_shrlock()
1122  *
1123  * See comments for smb_fsop_shrlock()
1124  */
1125 
1126 int
1127 smb_vop_shrlock(vnode_t *vp, uint32_t uniq_fid, uint32_t desired_access,
1128     uint32_t share_access, cred_t *cr)
1129 {
1130 	struct shrlock shr;
1131 	struct shr_locowner shr_own;
1132 	short new_access = 0;
1133 	short deny = 0;
1134 	int flag = 0;
1135 	int cmd;
1136 
1137 	cmd = (nbl_need_check(vp)) ? F_SHARE_NBMAND : F_SHARE;
1138 
1139 	/*
1140 	 * Check if this is a metadata access
1141 	 */
1142 
1143 	if ((desired_access & FILE_DATA_ALL) == 0) {
1144 		new_access |= F_MDACC;
1145 	} else {
1146 		if (desired_access & (ACE_READ_DATA | ACE_EXECUTE)) {
1147 			new_access |= F_RDACC;
1148 			flag |= FREAD;
1149 		}
1150 
1151 		if (desired_access & (ACE_WRITE_DATA | ACE_APPEND_DATA |
1152 		    ACE_ADD_FILE)) {
1153 			new_access |= F_WRACC;
1154 			flag |= FWRITE;
1155 		}
1156 
1157 		if (SMB_DENY_READ(share_access)) {
1158 			deny |= F_RDDNY;
1159 		}
1160 
1161 		if (SMB_DENY_WRITE(share_access)) {
1162 			deny |= F_WRDNY;
1163 		}
1164 
1165 		if (cmd == F_SHARE_NBMAND) {
1166 			if (desired_access & ACE_DELETE)
1167 				new_access |= F_RMACC;
1168 
1169 			if (SMB_DENY_DELETE(share_access)) {
1170 				deny |= F_RMDNY;
1171 			}
1172 		}
1173 	}
1174 
1175 	shr.s_access = new_access;
1176 	shr.s_deny = deny;
1177 	shr.s_sysid = smb_ct.cc_sysid;
1178 	shr.s_pid = uniq_fid;
1179 	shr.s_own_len = sizeof (shr_own);
1180 	shr.s_owner = (caddr_t)&shr_own;
1181 	shr_own.sl_id = shr.s_sysid;
1182 	shr_own.sl_pid = shr.s_pid;
1183 
1184 	return (VOP_SHRLOCK(vp, cmd, &shr, flag, cr, NULL));
1185 }
1186 
1187 int
1188 smb_vop_unshrlock(vnode_t *vp, uint32_t uniq_fid, cred_t *cr)
1189 {
1190 	struct shrlock shr;
1191 	struct shr_locowner shr_own;
1192 
1193 	/*
1194 	 * For s_access and s_deny, we do not need to pass in the original
1195 	 * values.
1196 	 */
1197 
1198 	shr.s_access = 0;
1199 	shr.s_deny = 0;
1200 	shr.s_sysid = smb_ct.cc_sysid;
1201 	shr.s_pid = uniq_fid;
1202 	shr.s_own_len = sizeof (shr_own);
1203 	shr.s_owner = (caddr_t)&shr_own;
1204 	shr_own.sl_id = shr.s_sysid;
1205 	shr_own.sl_pid = shr.s_pid;
1206 
1207 	return (VOP_SHRLOCK(vp, F_UNSHARE, &shr, 0, cr, NULL));
1208 }
1209 
1210 int
1211 smb_vop_frlock(vnode_t *vp, cred_t *cr, int flag, flock64_t *bf)
1212 {
1213 	int cmd = nbl_need_check(vp) ? F_SETLK_NBMAND : F_SETLK;
1214 	flk_callback_t flk_cb;
1215 
1216 	flk_init_callback(&flk_cb, smb_lock_frlock_callback, NULL);
1217 
1218 	return (VOP_FRLOCK(vp, cmd, bf, flag, 0, &flk_cb, cr, &smb_ct));
1219 }
1220 
1221 static callb_cpr_t *
1222 /* ARGSUSED */
1223 smb_lock_frlock_callback(flk_cb_when_t when, void *error)
1224 {
1225 	return (0);
1226 }
1227