xref: /illumos-gate/usr/src/lib/smbsrv/libfksmbsrv/common/fake_vop.c (revision 49d8359737352b52625c23836d8a4be4ae8e55dd)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
14  */
15 
16 #include <sys/types.h>
17 #include <sys/param.h>
18 #include <sys/systm.h>
19 #include <sys/t_lock.h>
20 #include <sys/errno.h>
21 #include <sys/cred.h>
22 #include <sys/user.h>
23 #include <sys/uio.h>
24 #include <sys/file.h>
25 #include <sys/pathname.h>
26 #include <sys/vfs.h>
27 #include <sys/vnode.h>
28 #include <sys/stat.h>
29 #include <sys/mode.h>
30 #include <sys/kmem.h>
31 #include <sys/cmn_err.h>
32 #include <sys/debug.h>
33 #include <sys/atomic.h>
34 #include <sys/acl.h>
35 #include <sys/filio.h>
36 #include <sys/flock.h>
37 #include <sys/nbmlock.h>
38 #include <sys/fcntl.h>
39 #include <sys/poll.h>
40 #include <sys/time.h>
41 
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <unistd.h>
45 
46 #include "vncache.h"
47 
48 #define	O_RWMASK	(O_WRONLY | O_RDWR) /* == 3 */
49 
50 int fop_shrlock_enable = 0;
51 
52 int stat_to_vattr(const struct stat *, vattr_t *);
53 int fop__getxvattr(vnode_t *, xvattr_t *);
54 int fop__setxvattr(vnode_t *, xvattr_t *);
55 
56 static void fake_inactive_xattrdir(vnode_t *);
57 
58 /* ARGSUSED */
59 int
60 fop_open(
61 	vnode_t **vpp,
62 	int mode,
63 	cred_t *cr,
64 	caller_context_t *ct)
65 {
66 
67 	if ((*vpp)->v_type == VREG) {
68 		if (mode & FREAD)
69 			atomic_add_32(&((*vpp)->v_rdcnt), 1);
70 		if (mode & FWRITE)
71 			atomic_add_32(&((*vpp)->v_wrcnt), 1);
72 	}
73 
74 	/* call to ->vop_open was here */
75 
76 	return (0);
77 }
78 
79 /* ARGSUSED */
80 int
81 fop_close(
82 	vnode_t *vp,
83 	int flag,
84 	int count,
85 	offset_t offset,
86 	cred_t *cr,
87 	caller_context_t *ct)
88 {
89 
90 	/* call to ->vop_close was here */
91 
92 	/*
93 	 * Check passed in count to handle possible dups. Vnode counts are only
94 	 * kept on regular files
95 	 */
96 	if ((vp->v_type == VREG) && (count == 1))  {
97 		if (flag & FREAD) {
98 			ASSERT(vp->v_rdcnt > 0);
99 			atomic_add_32(&(vp->v_rdcnt), -1);
100 		}
101 		if (flag & FWRITE) {
102 			ASSERT(vp->v_wrcnt > 0);
103 			atomic_add_32(&(vp->v_wrcnt), -1);
104 		}
105 	}
106 	return (0);
107 }
108 
109 /* ARGSUSED */
110 int
111 fop_read(
112 	vnode_t *vp,
113 	uio_t *uio,
114 	int ioflag,
115 	cred_t *cr,
116 	caller_context_t *ct)
117 {
118 	struct stat st;
119 	struct iovec *iov;
120 	ssize_t resid;
121 	size_t cnt;
122 	int n;
123 
124 	/*
125 	 * If that caller asks for read beyond end of file,
126 	 * that causes the pread call to block.  (Ugh!)
127 	 * Get the file size and return what we can.
128 	 */
129 	(void) fstat(vp->v_fd, &st);
130 	resid = uio->uio_resid;
131 	if ((uio->uio_loffset + resid) > st.st_size)
132 		resid = st.st_size - uio->uio_loffset;
133 
134 	while (resid > 0) {
135 
136 		ASSERT(uio->uio_iovcnt > 0);
137 		iov = uio->uio_iov;
138 
139 		if (iov->iov_len == 0) {
140 			uio->uio_iov++;
141 			uio->uio_iovcnt--;
142 			continue;
143 		}
144 		cnt = iov->iov_len;
145 		if (cnt > resid)
146 			cnt = resid;
147 
148 		n = pread(vp->v_fd, iov->iov_base, cnt, uio->uio_loffset);
149 		if (n < 0)
150 			return (errno);
151 
152 		iov->iov_base += n;
153 		iov->iov_len -= n;
154 
155 		uio->uio_resid -= n;
156 		uio->uio_loffset += n;
157 
158 		resid -= n;
159 	}
160 
161 	return (0);
162 }
163 
164 /* ARGSUSED */
165 int
166 fop_write(
167 	vnode_t *vp,
168 	uio_t *uio,
169 	int ioflag,
170 	cred_t *cr,
171 	caller_context_t *ct)
172 {
173 	struct iovec *iov;
174 	size_t cnt;
175 	int n;
176 
177 	while (uio->uio_resid > 0) {
178 
179 		ASSERT(uio->uio_iovcnt > 0);
180 		iov = uio->uio_iov;
181 
182 		if (iov->iov_len == 0) {
183 			uio->uio_iov++;
184 			uio->uio_iovcnt--;
185 			continue;
186 		}
187 		cnt = iov->iov_len;
188 		if (cnt > uio->uio_resid)
189 			cnt = uio->uio_resid;
190 
191 		n = pwrite(vp->v_fd, iov->iov_base, iov->iov_len,
192 		    uio->uio_loffset);
193 		if (n < 0)
194 			return (errno);
195 
196 		iov->iov_base += n;
197 		iov->iov_len -= n;
198 
199 		uio->uio_resid -= n;
200 		uio->uio_loffset += n;
201 	}
202 
203 	if (ioflag == FSYNC) {
204 		(void) fsync(vp->v_fd);
205 	}
206 
207 	return (0);
208 }
209 
210 /* ARGSUSED */
211 int
212 fop_ioctl(
213 	vnode_t *vp,
214 	int cmd,
215 	intptr_t arg,
216 	int flag,
217 	cred_t *cr,
218 	int *rvalp,
219 	caller_context_t *ct)
220 {
221 	return (ENOSYS);
222 }
223 
224 /* ARGSUSED */
225 int
226 fop_setfl(
227 	vnode_t *vp,
228 	int oflags,
229 	int nflags,
230 	cred_t *cr,
231 	caller_context_t *ct)
232 {
233 	/* allow any flags? See fs_setfl */
234 	return (0);
235 }
236 
237 /* ARGSUSED */
238 int
239 fop_getattr(
240 	vnode_t *vp,
241 	vattr_t *vap,
242 	int flags,
243 	cred_t *cr,
244 	caller_context_t *ct)
245 {
246 	int error;
247 	struct stat st;
248 
249 	if (fstat(vp->v_fd, &st) == -1)
250 		return (errno);
251 	error = stat_to_vattr(&st, vap);
252 
253 	if (vap->va_mask & AT_XVATTR)
254 		(void) fop__getxvattr(vp, (xvattr_t *)vap);
255 
256 	return (error);
257 }
258 
259 /* ARGSUSED */
260 int
261 fop_setattr(
262 	vnode_t *vp,
263 	vattr_t *vap,
264 	int flags,
265 	cred_t *cr,
266 	caller_context_t *ct)
267 {
268 	timespec_t times[2];
269 
270 	if (vap->va_mask & AT_SIZE) {
271 		if (ftruncate(vp->v_fd, vap->va_size) == -1)
272 			return (errno);
273 	}
274 
275 	/* AT_MODE or anything else? */
276 
277 	if (vap->va_mask & AT_XVATTR)
278 		(void) fop__setxvattr(vp, (xvattr_t *)vap);
279 
280 	if (vap->va_mask & (AT_ATIME | AT_MTIME)) {
281 		if (vap->va_mask & AT_ATIME) {
282 			times[0] = vap->va_atime;
283 		} else {
284 			times[0].tv_sec = 0;
285 			times[0].tv_nsec = UTIME_OMIT;
286 		}
287 		if (vap->va_mask & AT_MTIME) {
288 			times[1] = vap->va_mtime;
289 		} else {
290 			times[1].tv_sec = 0;
291 			times[1].tv_nsec = UTIME_OMIT;
292 		}
293 
294 		(void) futimens(vp->v_fd, times);
295 	}
296 
297 	return (0);
298 }
299 
300 /* ARGSUSED */
301 int
302 fop_access(
303 	vnode_t *vp,
304 	int mode,
305 	int flags,
306 	cred_t *cr,
307 	caller_context_t *ct)
308 {
309 	return (0);
310 }
311 
312 /*
313  * Conceptually like xattr_dir_lookup()
314  */
315 static int
316 fake_lookup_xattrdir(
317 	vnode_t *dvp,
318 	vnode_t **vpp)
319 {
320 	int len, fd;
321 	int omode = O_RDWR | O_NOFOLLOW;
322 	vnode_t *vp;
323 
324 	*vpp = NULL;
325 
326 	if (dvp->v_type != VDIR && dvp->v_type != VREG)
327 		return (EINVAL);
328 
329 	/*
330 	 * If we're already in sysattr space, don't allow creation
331 	 * of another level of sysattrs.
332 	 */
333 	if (dvp->v_flag & V_SYSATTR)
334 		return (EINVAL);
335 
336 	mutex_enter(&dvp->v_lock);
337 	if (dvp->v_xattrdir != NULL) {
338 		*vpp = dvp->v_xattrdir;
339 		VN_HOLD(*vpp);
340 		mutex_exit(&dvp->v_lock);
341 		return (0);
342 	}
343 	mutex_exit(&dvp->v_lock);
344 
345 	omode = O_RDONLY|O_XATTR;
346 	fd = openat(dvp->v_fd, ".", omode);
347 	if (fd < 0)
348 		return (errno);
349 
350 	vp = vn_alloc(KM_SLEEP);
351 	vp->v_fd = fd;
352 	vp->v_flag = V_XATTRDIR|V_SYSATTR;
353 	vp->v_type = VDIR;
354 	vp->v_vfsp = dvp->v_vfsp;
355 
356 	/* Set v_path to parent path + "/@" (like NFS) */
357 	len = strlen(dvp->v_path) + 3;
358 	vp->v_path = kmem_alloc(len, KM_SLEEP);
359 	(void) snprintf(vp->v_path, len, "%s/@", dvp->v_path);
360 
361 	/*
362 	 * Keep a pointer to the parent and a hold on it.
363 	 * Both are cleaned up in fake_inactive_xattrdir
364 	 */
365 	vp->v_data = dvp;
366 	vn_hold(dvp);
367 
368 	mutex_enter(&dvp->v_lock);
369 	if (dvp->v_xattrdir == NULL) {
370 		*vpp = dvp->v_xattrdir = vp;
371 		mutex_exit(&dvp->v_lock);
372 	} else {
373 		*vpp = dvp->v_xattrdir;
374 		mutex_exit(&dvp->v_lock);
375 		fake_inactive_xattrdir(vp);
376 	}
377 
378 	return (0);
379 }
380 
381 /* ARGSUSED */
382 int
383 fop_lookup(
384 	vnode_t *dvp,
385 	char *name,
386 	vnode_t **vpp,
387 	pathname_t *pnp,
388 	int flags,
389 	vnode_t *rdir,
390 	cred_t *cr,
391 	caller_context_t *ct,
392 	int *deflags,		/* Returned per-dirent flags */
393 	pathname_t *ppnp)	/* Returned case-preserved name in directory */
394 {
395 	int fd;
396 	int omode = O_RDWR | O_NOFOLLOW;
397 	vnode_t *vp;
398 	struct stat st;
399 
400 	if (flags & LOOKUP_XATTR)
401 		return (fake_lookup_xattrdir(dvp, vpp));
402 
403 	/*
404 	 * If lookup is for "", just return dvp.
405 	 */
406 	if (name[0] == '\0') {
407 		vn_hold(dvp);
408 		*vpp = dvp;
409 		return (0);
410 	}
411 
412 	if (fstatat(dvp->v_fd, name, &st, AT_SYMLINK_NOFOLLOW) == -1)
413 		return (errno);
414 
415 	vp = vncache_lookup(&st);
416 	if (vp != NULL) {
417 		/* lookup gave us a hold */
418 		*vpp = vp;
419 		return (0);
420 	}
421 
422 	if (S_ISDIR(st.st_mode))
423 		omode = O_RDONLY | O_NOFOLLOW;
424 
425 again:
426 	fd = openat(dvp->v_fd, name, omode, 0);
427 	if (fd < 0) {
428 		if ((omode & O_RWMASK) == O_RDWR) {
429 			omode &= ~O_RWMASK;
430 			omode |= O_RDONLY;
431 			goto again;
432 		}
433 		return (errno);
434 	}
435 
436 	if (fstat(fd, &st) == -1) {
437 		(void) close(fd);
438 		return (errno);
439 	}
440 
441 	vp = vncache_enter(&st, dvp, name, fd);
442 
443 	*vpp = vp;
444 	return (0);
445 }
446 
447 /* ARGSUSED */
448 int
449 fop_create(
450 	vnode_t *dvp,
451 	char *name,
452 	vattr_t *vap,
453 	vcexcl_t excl,
454 	int mode,
455 	vnode_t **vpp,
456 	cred_t *cr,
457 	int flags,
458 	caller_context_t *ct,
459 	vsecattr_t *vsecp)	/* ACL to set during create */
460 {
461 	struct stat st;
462 	vnode_t *vp;
463 	int err, fd, omode;
464 
465 	/*
466 	 * If creating "", just return dvp.
467 	 */
468 	if (name[0] == '\0') {
469 		vn_hold(dvp);
470 		*vpp = dvp;
471 		return (0);
472 	}
473 
474 	err = fstatat(dvp->v_fd, name, &st, AT_SYMLINK_NOFOLLOW);
475 	if (err != 0)
476 		err = errno;
477 
478 	vp = NULL;
479 	if (err == 0) {
480 		/* The file already exists. */
481 		if (excl == EXCL)
482 			return (EEXIST);
483 
484 		vp = vncache_lookup(&st);
485 		/* vp gained a hold */
486 	}
487 
488 	if (vp == NULL) {
489 		/*
490 		 * Open it. (may or may not exist)
491 		 */
492 		omode = O_RDWR | O_CREAT | O_NOFOLLOW;
493 		if (excl == EXCL)
494 			omode |= O_EXCL;
495 	open_again:
496 		fd = openat(dvp->v_fd, name, omode, mode);
497 		if (fd < 0) {
498 			if ((omode & O_RWMASK) == O_RDWR) {
499 				omode &= ~O_RWMASK;
500 				omode |= O_RDONLY;
501 				goto open_again;
502 			}
503 			return (errno);
504 		}
505 		(void) fstat(fd, &st);
506 
507 		vp = vncache_enter(&st, dvp, name, fd);
508 		/* vp has its initial hold */
509 	}
510 
511 	/* Should have the vp now. */
512 	if (vp == NULL)
513 		return (EFAULT);
514 
515 	if (vp->v_type == VDIR && vap->va_type != VDIR) {
516 		vn_rele(vp);
517 		return (EISDIR);
518 	}
519 	if (vp->v_type != VDIR && vap->va_type == VDIR) {
520 		vn_rele(vp);
521 		return (ENOTDIR);
522 	}
523 
524 	/*
525 	 * Might need to set attributes.
526 	 */
527 	(void) fop_setattr(vp, vap, 0, cr, ct);
528 
529 	*vpp = vp;
530 	return (0);
531 }
532 
533 /* ARGSUSED */
534 int
535 fop_remove(
536 	vnode_t *dvp,
537 	char *name,
538 	cred_t *cr,
539 	caller_context_t *ct,
540 	int flags)
541 {
542 
543 	if (unlinkat(dvp->v_fd, name, 0))
544 		return (errno);
545 
546 	return (0);
547 }
548 
549 /* ARGSUSED */
550 int
551 fop_link(
552 	vnode_t *to_dvp,
553 	vnode_t *fr_vp,
554 	char *to_name,
555 	cred_t *cr,
556 	caller_context_t *ct,
557 	int flags)
558 {
559 	int err;
560 
561 	/*
562 	 * Would prefer to specify "from" as the combination:
563 	 * (fr_vp->v_fd, NULL) but linkat does not permit it.
564 	 */
565 	err = linkat(AT_FDCWD, fr_vp->v_path, to_dvp->v_fd, to_name,
566 	    AT_SYMLINK_FOLLOW);
567 	if (err == -1)
568 		err = errno;
569 
570 	return (err);
571 }
572 
573 /* ARGSUSED */
574 int
575 fop_rename(
576 	vnode_t *from_dvp,
577 	char *from_name,
578 	vnode_t *to_dvp,
579 	char *to_name,
580 	cred_t *cr,
581 	caller_context_t *ct,
582 	int flags)
583 {
584 	struct stat st;
585 	vnode_t *vp;
586 	int err;
587 
588 	if (fstatat(from_dvp->v_fd, from_name, &st,
589 	    AT_SYMLINK_NOFOLLOW) == -1)
590 		return (errno);
591 
592 	vp = vncache_lookup(&st);
593 	if (vp == NULL)
594 		return (ENOENT);
595 
596 	err = renameat(from_dvp->v_fd, from_name, to_dvp->v_fd, to_name);
597 	if (err == -1)
598 		err = errno;
599 	else
600 		vncache_renamed(vp, to_dvp, to_name);
601 
602 	vn_rele(vp);
603 
604 	return (err);
605 }
606 
607 /* ARGSUSED */
608 int
609 fop_mkdir(
610 	vnode_t *dvp,
611 	char *name,
612 	vattr_t *vap,
613 	vnode_t **vpp,
614 	cred_t *cr,
615 	caller_context_t *ct,
616 	int flags,
617 	vsecattr_t *vsecp)	/* ACL to set during create */
618 {
619 	struct stat st;
620 	int err, fd;
621 
622 	mode_t mode = vap->va_mode & 0777;
623 
624 	if (mkdirat(dvp->v_fd, name, mode) == -1)
625 		return (errno);
626 
627 	if ((fd = openat(dvp->v_fd, name, O_RDONLY)) == -1)
628 		return (errno);
629 	if (fstat(fd, &st) == -1) {
630 		err = errno;
631 		(void) close(fd);
632 		return (err);
633 	}
634 
635 	*vpp = vncache_enter(&st, dvp, name, fd);
636 
637 	/*
638 	 * Might need to set attributes.
639 	 */
640 	(void) fop_setattr(*vpp, vap, 0, cr, ct);
641 
642 	return (0);
643 }
644 
645 /* ARGSUSED */
646 int
647 fop_rmdir(
648 	vnode_t *dvp,
649 	char *name,
650 	vnode_t *cdir,
651 	cred_t *cr,
652 	caller_context_t *ct,
653 	int flags)
654 {
655 
656 	if (unlinkat(dvp->v_fd, name, AT_REMOVEDIR) == -1)
657 		return (errno);
658 
659 	return (0);
660 }
661 
662 /* ARGSUSED */
663 int
664 fop_readdir(
665 	vnode_t *vp,
666 	uio_t *uiop,
667 	cred_t *cr,
668 	int *eofp,
669 	caller_context_t *ct,
670 	int flags)
671 {
672 	struct iovec *iov;
673 	int cnt;
674 	int error = 0;
675 	int fd = vp->v_fd;
676 
677 	if (eofp) {
678 		*eofp = 0;
679 	}
680 
681 	error = lseek(fd, uiop->uio_loffset, SEEK_SET);
682 	if (error == -1)
683 		return (errno);
684 
685 	ASSERT(uiop->uio_iovcnt > 0);
686 	iov = uiop->uio_iov;
687 	if (iov->iov_len < sizeof (struct dirent))
688 		return (EINVAL);
689 
690 	/* LINTED E_BAD_PTR_CAST_ALIGN */
691 	cnt = getdents(fd, (struct dirent *)(uiop->uio_iov->iov_base),
692 	    uiop->uio_resid);
693 	if (cnt == -1)
694 		return (errno);
695 	if (cnt == 0) {
696 		if (eofp) {
697 			*eofp = 1;
698 		}
699 		return (ENOENT);
700 	}
701 
702 	iov->iov_base += cnt;
703 	iov->iov_len  -= cnt;
704 	uiop->uio_resid -= cnt;
705 	uiop->uio_loffset = lseek(fd, 0LL, SEEK_CUR);
706 
707 	return (0);
708 }
709 
710 /* ARGSUSED */
711 int
712 fop_symlink(
713 	vnode_t *dvp,
714 	char *linkname,
715 	vattr_t *vap,
716 	char *target,
717 	cred_t *cr,
718 	caller_context_t *ct,
719 	int flags)
720 {
721 	return (ENOSYS);
722 }
723 
724 /* ARGSUSED */
725 int
726 fop_readlink(
727 	vnode_t *vp,
728 	uio_t *uiop,
729 	cred_t *cr,
730 	caller_context_t *ct)
731 {
732 	return (ENOSYS);
733 }
734 
735 /* ARGSUSED */
736 int
737 fop_fsync(
738 	vnode_t *vp,
739 	int syncflag,
740 	cred_t *cr,
741 	caller_context_t *ct)
742 {
743 
744 	if (fsync(vp->v_fd) == -1)
745 		return (errno);
746 
747 	return (0);
748 }
749 
750 /* ARGSUSED */
751 void
752 fop_inactive(
753 	vnode_t *vp,
754 	cred_t *cr,
755 	caller_context_t *ct)
756 {
757 	if (vp->v_flag & V_XATTRDIR) {
758 		fake_inactive_xattrdir(vp);
759 	} else {
760 		vncache_inactive(vp);
761 	}
762 }
763 
764 /*
765  * The special xattr directories are not in the vncache AVL, but
766  * hang off the parent's v_xattrdir field.  When vn_rele finds
767  * an xattr dir at v_count == 1 it calls here, but until we
768  * take locks on both the parent and the xattrdir, we don't
769  * know if we're really at the last reference.  So in here we
770  * take both locks, re-check the count, and either bail out
771  * or proceed with "inactive" vnode cleanup.  Part of that
772  * cleanup includes releasing the hold on the parent and
773  * clearing the parent's v_xattrdir field, which were
774  * setup in fake_lookup_xattrdir()
775  */
776 static void
777 fake_inactive_xattrdir(vnode_t *vp)
778 {
779 	vnode_t *dvp = vp->v_data; /* parent */
780 	mutex_enter(&dvp->v_lock);
781 	mutex_enter(&vp->v_lock);
782 	if (vp->v_count > 1) {
783 		/* new ref. via v_xattrdir */
784 		mutex_exit(&vp->v_lock);
785 		mutex_exit(&dvp->v_lock);
786 		return;
787 	}
788 	ASSERT(dvp->v_xattrdir == vp);
789 	dvp->v_xattrdir = NULL;
790 	mutex_exit(&vp->v_lock);
791 	mutex_exit(&dvp->v_lock);
792 	vn_rele(dvp);
793 	vn_free(vp);
794 }
795 
796 /* ARGSUSED */
797 int
798 fop_fid(
799 	vnode_t *vp,
800 	fid_t *fidp,
801 	caller_context_t *ct)
802 {
803 	return (ENOSYS);
804 }
805 
806 /* ARGSUSED */
807 int
808 fop_rwlock(
809 	vnode_t *vp,
810 	int write_lock,
811 	caller_context_t *ct)
812 {
813 	/* See: fs_rwlock */
814 	return (-1);
815 }
816 
817 /* ARGSUSED */
818 void
819 fop_rwunlock(
820 	vnode_t *vp,
821 	int write_lock,
822 	caller_context_t *ct)
823 {
824 	/* See: fs_rwunlock */
825 }
826 
827 /* ARGSUSED */
828 int
829 fop_seek(
830 	vnode_t *vp,
831 	offset_t ooff,
832 	offset_t *noffp,
833 	caller_context_t *ct)
834 {
835 	return (ENOSYS);
836 }
837 
838 /* ARGSUSED */
839 int
840 fop_cmp(
841 	vnode_t *vp1,
842 	vnode_t *vp2,
843 	caller_context_t *ct)
844 {
845 	/* See fs_cmp */
846 	return (vncache_cmp(vp1, vp2));
847 }
848 
849 /* ARGSUSED */
850 int
851 fop_frlock(
852 	vnode_t *vp,
853 	int cmd,
854 	flock64_t *bfp,
855 	int flag,
856 	offset_t offset,
857 	struct flk_callback *flk_cbp,
858 	cred_t *cr,
859 	caller_context_t *ct)
860 {
861 #if defined(_LP64)
862 	offset_t maxoffset = INT64_MAX;
863 #elif defined(_ILP32)
864 	/*
865 	 * Sadly, the fcntl API enforces 32-bit offsets,
866 	 * even though we have _FILE_OFFSET_BITS=64
867 	 */
868 	offset_t maxoffset = INT32_MAX;
869 #else
870 #error "unsupported env."
871 #endif
872 
873 	/* See fs_frlock */
874 
875 	switch (cmd) {
876 	case F_GETLK:
877 	case F_SETLK_NBMAND:
878 	case F_SETLK:
879 	case F_SETLKW:
880 		break;
881 	default:
882 		return (EINVAL);
883 	}
884 
885 	/* We only get SEEK_SET ranges here. */
886 	if (bfp->l_whence != 0)
887 		return (EINVAL);
888 
889 	/*
890 	 * One limitation of using fcntl(2) F_SETLK etc is that
891 	 * the real kernel limits the offsets we can use.
892 	 * (Maybe the fcntl API should loosen that up?)
893 	 * See syscall/fcntl.c:flock_check()
894 	 *
895 	 * Here in libfksmbsrv we can just ignore such locks,
896 	 * or ignore the part that extends beyond maxoffset.
897 	 * The SMB layer still keeps track of such locks for
898 	 * conflict detection, so not reflecting such locks
899 	 * into the real FS layer is OK.  Note: this may
900 	 * modify the pased bfp->l_len.
901 	 */
902 	if (bfp->l_start < 0 || bfp->l_start > maxoffset)
903 		return (0);
904 	if (bfp->l_len < 0 || bfp->l_len > maxoffset)
905 		return (0);
906 	if (bfp->l_len > (maxoffset - bfp->l_start + 1))
907 		bfp->l_len = (maxoffset - bfp->l_start + 1);
908 
909 	if (fcntl(vp->v_fd, cmd, bfp) == -1)
910 		return (errno);
911 
912 	return (0);
913 }
914 
915 /* ARGSUSED */
916 int
917 fop_space(
918 	vnode_t *vp,
919 	int cmd,
920 	flock64_t *bfp,
921 	int flag,
922 	offset_t offset,
923 	cred_t *cr,
924 	caller_context_t *ct)
925 {
926 	/* See fs_frlock */
927 
928 	switch (cmd) {
929 	case F_ALLOCSP:
930 	case F_FREESP:
931 		break;
932 	default:
933 		return (EINVAL);
934 	}
935 
936 	if (fcntl(vp->v_fd, cmd, bfp) == -1)
937 		return (errno);
938 
939 	return (0);
940 }
941 
942 /* ARGSUSED */
943 int
944 fop_realvp(
945 	vnode_t *vp,
946 	vnode_t **vpp,
947 	caller_context_t *ct)
948 {
949 	return (ENOSYS);
950 }
951 
952 /* ARGSUSED */
953 int
954 fop_getpage(
955 	vnode_t *vp,
956 	offset_t off,
957 	size_t len,
958 	uint_t *protp,
959 	struct page **plarr,
960 	size_t plsz,
961 	struct seg *seg,
962 	caddr_t addr,
963 	enum seg_rw rw,
964 	cred_t *cr,
965 	caller_context_t *ct)
966 {
967 	return (ENOSYS);
968 }
969 
970 /* ARGSUSED */
971 int
972 fop_putpage(
973 	vnode_t *vp,
974 	offset_t off,
975 	size_t len,
976 	int flags,
977 	cred_t *cr,
978 	caller_context_t *ct)
979 {
980 	return (ENOSYS);
981 }
982 
983 /* ARGSUSED */
984 int
985 fop_map(
986 	vnode_t *vp,
987 	offset_t off,
988 	struct as *as,
989 	caddr_t *addrp,
990 	size_t len,
991 	uchar_t prot,
992 	uchar_t maxprot,
993 	uint_t flags,
994 	cred_t *cr,
995 	caller_context_t *ct)
996 {
997 	return (ENOSYS);
998 }
999 
1000 /* ARGSUSED */
1001 int
1002 fop_addmap(
1003 	vnode_t *vp,
1004 	offset_t off,
1005 	struct as *as,
1006 	caddr_t addr,
1007 	size_t len,
1008 	uchar_t prot,
1009 	uchar_t maxprot,
1010 	uint_t flags,
1011 	cred_t *cr,
1012 	caller_context_t *ct)
1013 {
1014 	return (ENOSYS);
1015 }
1016 
1017 /* ARGSUSED */
1018 int
1019 fop_delmap(
1020 	vnode_t *vp,
1021 	offset_t off,
1022 	struct as *as,
1023 	caddr_t addr,
1024 	size_t len,
1025 	uint_t prot,
1026 	uint_t maxprot,
1027 	uint_t flags,
1028 	cred_t *cr,
1029 	caller_context_t *ct)
1030 {
1031 	return (ENOSYS);
1032 }
1033 
1034 /* ARGSUSED */
1035 int
1036 fop_poll(
1037 	vnode_t *vp,
1038 	short events,
1039 	int anyyet,
1040 	short *reventsp,
1041 	struct pollhead **phpp,
1042 	caller_context_t *ct)
1043 {
1044 	*reventsp = 0;
1045 	if (events & POLLIN)
1046 		*reventsp |= POLLIN;
1047 	if (events & POLLRDNORM)
1048 		*reventsp |= POLLRDNORM;
1049 	if (events & POLLRDBAND)
1050 		*reventsp |= POLLRDBAND;
1051 	if (events & POLLOUT)
1052 		*reventsp |= POLLOUT;
1053 	if (events & POLLWRBAND)
1054 		*reventsp |= POLLWRBAND;
1055 	*phpp = NULL; /* or fake_pollhead? */
1056 
1057 	return (0);
1058 }
1059 
1060 /* ARGSUSED */
1061 int
1062 fop_dump(
1063 	vnode_t *vp,
1064 	caddr_t addr,
1065 	offset_t lbdn,
1066 	offset_t dblks,
1067 	caller_context_t *ct)
1068 {
1069 	return (ENOSYS);
1070 }
1071 
1072 /*
1073  * See fs_pathconf
1074  */
1075 /* ARGSUSED */
1076 int
1077 fop_pathconf(
1078 	vnode_t *vp,
1079 	int cmd,
1080 	ulong_t *valp,
1081 	cred_t *cr,
1082 	caller_context_t *ct)
1083 {
1084 	register ulong_t val;
1085 	register int error = 0;
1086 
1087 	switch (cmd) {
1088 
1089 	case _PC_LINK_MAX:
1090 		val = MAXLINK;
1091 		break;
1092 
1093 	case _PC_MAX_CANON:
1094 		val = MAX_CANON;
1095 		break;
1096 
1097 	case _PC_MAX_INPUT:
1098 		val = MAX_INPUT;
1099 		break;
1100 
1101 	case _PC_NAME_MAX:
1102 		val = MAXNAMELEN;
1103 		break;
1104 
1105 	case _PC_PATH_MAX:
1106 	case _PC_SYMLINK_MAX:
1107 		val = MAXPATHLEN;
1108 		break;
1109 
1110 	case _PC_PIPE_BUF:
1111 		val = PIPE_BUF;
1112 		break;
1113 
1114 	case _PC_NO_TRUNC:
1115 		val = (ulong_t)-1;
1116 		break;
1117 
1118 	case _PC_VDISABLE:
1119 		val = _POSIX_VDISABLE;
1120 		break;
1121 
1122 	case _PC_CHOWN_RESTRICTED:
1123 		val = 1; /* chown restricted enabled */
1124 		break;
1125 
1126 	case _PC_FILESIZEBITS:
1127 		val = (ulong_t)-1;    /* large file support */
1128 		break;
1129 
1130 	case _PC_ACL_ENABLED:
1131 		val = _ACL_ACE_ENABLED;
1132 		break;
1133 
1134 	case _PC_CASE_BEHAVIOR:
1135 		val = _CASE_SENSITIVE;
1136 		break;
1137 
1138 	case _PC_SATTR_ENABLED:
1139 	case _PC_SATTR_EXISTS:
1140 		val = 0;
1141 		break;
1142 
1143 	case _PC_ACCESS_FILTERING:
1144 		val = 0;
1145 		break;
1146 
1147 	default:
1148 		error = EINVAL;
1149 		break;
1150 	}
1151 
1152 	if (error == 0)
1153 		*valp = val;
1154 	return (error);
1155 }
1156 
1157 /* ARGSUSED */
1158 int
1159 fop_pageio(
1160 	vnode_t *vp,
1161 	struct page *pp,
1162 	u_offset_t io_off,
1163 	size_t io_len,
1164 	int flags,
1165 	cred_t *cr,
1166 	caller_context_t *ct)
1167 {
1168 	return (ENOSYS);
1169 }
1170 
1171 /* ARGSUSED */
1172 int
1173 fop_dumpctl(
1174 	vnode_t *vp,
1175 	int action,
1176 	offset_t *blkp,
1177 	caller_context_t *ct)
1178 {
1179 	return (ENOSYS);
1180 }
1181 
1182 /* ARGSUSED */
1183 void
1184 fop_dispose(
1185 	vnode_t *vp,
1186 	struct page *pp,
1187 	int flag,
1188 	int dn,
1189 	cred_t *cr,
1190 	caller_context_t *ct)
1191 {
1192 }
1193 
1194 /* ARGSUSED */
1195 int
1196 fop_setsecattr(
1197 	vnode_t *vp,
1198 	vsecattr_t *vsap,
1199 	int flag,
1200 	cred_t *cr,
1201 	caller_context_t *ct)
1202 {
1203 	return (0);
1204 }
1205 
1206 /*
1207  * Fake up just enough of this so we can test get/set SDs.
1208  */
1209 /* ARGSUSED */
1210 int
1211 fop_getsecattr(
1212 	vnode_t *vp,
1213 	vsecattr_t *vsecattr,
1214 	int flag,
1215 	cred_t *cr,
1216 	caller_context_t *ct)
1217 {
1218 
1219 	vsecattr->vsa_aclcnt	= 0;
1220 	vsecattr->vsa_aclentsz	= 0;
1221 	vsecattr->vsa_aclentp	= NULL;
1222 	vsecattr->vsa_dfaclcnt	= 0;	/* Default ACLs are not fabricated */
1223 	vsecattr->vsa_dfaclentp	= NULL;
1224 
1225 	if (vsecattr->vsa_mask & (VSA_ACLCNT | VSA_ACL)) {
1226 		aclent_t *aclentp;
1227 		size_t aclsize;
1228 
1229 		aclsize = sizeof (aclent_t);
1230 		vsecattr->vsa_aclcnt = 1;
1231 		vsecattr->vsa_aclentp = kmem_zalloc(aclsize, KM_SLEEP);
1232 		aclentp = vsecattr->vsa_aclentp;
1233 
1234 		aclentp->a_type = OTHER_OBJ;
1235 		aclentp->a_perm = 0777;
1236 		aclentp->a_id = (gid_t)-1;
1237 		aclentp++;
1238 	} else if (vsecattr->vsa_mask & (VSA_ACECNT | VSA_ACE)) {
1239 		ace_t *acl;
1240 
1241 		acl = kmem_alloc(sizeof (ace_t), KM_SLEEP);
1242 		acl->a_who = (uint32_t)-1;
1243 		acl->a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
1244 		acl->a_flags = ACE_EVERYONE;
1245 		acl->a_access_mask  = ACE_MODIFY_PERMS;
1246 
1247 		vsecattr->vsa_aclentp = (void *)acl;
1248 		vsecattr->vsa_aclcnt = 1;
1249 		vsecattr->vsa_aclentsz = sizeof (ace_t);
1250 	}
1251 
1252 	return (0);
1253 }
1254 
1255 /* ARGSUSED */
1256 int
1257 fop_shrlock(
1258 	vnode_t *vp,
1259 	int cmd,
1260 	struct shrlock *shr,
1261 	int flag,
1262 	cred_t *cr,
1263 	caller_context_t *ct)
1264 {
1265 
1266 	switch (cmd) {
1267 	case F_SHARE:
1268 	case F_SHARE_NBMAND:
1269 	case F_UNSHARE:
1270 		break;
1271 	default:
1272 		return (EINVAL);
1273 	}
1274 
1275 	if (!fop_shrlock_enable)
1276 		return (0);
1277 
1278 	if (fcntl(vp->v_fd, cmd, shr) == -1)
1279 		return (errno);
1280 
1281 	return (0);
1282 }
1283 
1284 /* ARGSUSED */
1285 int
1286 fop_vnevent(vnode_t *vp, vnevent_t vnevent, vnode_t *dvp, char *fnm,
1287     caller_context_t *ct)
1288 {
1289 	return (ENOSYS);
1290 }
1291 
1292 /* ARGSUSED */
1293 int
1294 fop_reqzcbuf(vnode_t *vp, enum uio_rw ioflag, xuio_t *uiop, cred_t *cr,
1295     caller_context_t *ct)
1296 {
1297 	return (ENOSYS);
1298 }
1299 
1300 /* ARGSUSED */
1301 int
1302 fop_retzcbuf(vnode_t *vp, xuio_t *uiop, cred_t *cr, caller_context_t *ct)
1303 {
1304 	return (ENOSYS);
1305 }
1306 
1307 
1308 /*
1309  * ***************************************************************
1310  * other VOP support
1311  */
1312 
1313 /*
1314  * Convert stat(2) formats to vnode types and vice versa.  (Knows about
1315  * numerical order of S_IFMT and vnode types.)
1316  */
1317 enum vtype iftovt_tab[] = {
1318 	VNON, VFIFO, VCHR, VNON, VDIR, VNON, VBLK, VNON,
1319 	VREG, VNON, VLNK, VNON, VSOCK, VNON, VNON, VNON
1320 };
1321 
1322 ushort_t vttoif_tab[] = {
1323 	0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFIFO,
1324 	S_IFDOOR, 0, S_IFSOCK, S_IFPORT, 0
1325 };
1326 
1327 /*
1328  * stat_to_vattr()
1329  *
1330  * Convert from a stat structure to an vattr structure
1331  * Note: only set fields according to va_mask
1332  */
1333 
1334 int
1335 stat_to_vattr(const struct stat *st, vattr_t *vap)
1336 {
1337 
1338 	if (vap->va_mask & AT_TYPE)
1339 		vap->va_type = IFTOVT(st->st_mode);
1340 
1341 	if (vap->va_mask & AT_MODE)
1342 		vap->va_mode = st->st_mode;
1343 
1344 	if (vap->va_mask & AT_UID)
1345 		vap->va_uid = st->st_uid;
1346 
1347 	if (vap->va_mask & AT_GID)
1348 		vap->va_gid = st->st_gid;
1349 
1350 	if (vap->va_mask & AT_FSID)
1351 		vap->va_fsid = st->st_dev;
1352 
1353 	if (vap->va_mask & AT_NODEID)
1354 		vap->va_nodeid = st->st_ino;
1355 
1356 	if (vap->va_mask & AT_NLINK)
1357 		vap->va_nlink = st->st_nlink;
1358 
1359 	if (vap->va_mask & AT_SIZE)
1360 		vap->va_size = (u_offset_t)st->st_size;
1361 
1362 	if (vap->va_mask & AT_ATIME) {
1363 		vap->va_atime.tv_sec  = st->st_atim.tv_sec;
1364 		vap->va_atime.tv_nsec = st->st_atim.tv_nsec;
1365 	}
1366 
1367 	if (vap->va_mask & AT_MTIME) {
1368 		vap->va_mtime.tv_sec  = st->st_mtim.tv_sec;
1369 		vap->va_mtime.tv_nsec = st->st_mtim.tv_nsec;
1370 	}
1371 
1372 	if (vap->va_mask & AT_CTIME) {
1373 		vap->va_ctime.tv_sec  = st->st_ctim.tv_sec;
1374 		vap->va_ctime.tv_nsec = st->st_ctim.tv_nsec;
1375 	}
1376 
1377 	if (vap->va_mask & AT_RDEV)
1378 		vap->va_rdev = st->st_rdev;
1379 
1380 	if (vap->va_mask & AT_BLKSIZE)
1381 		vap->va_blksize = (uint_t)st->st_blksize;
1382 
1383 
1384 	if (vap->va_mask & AT_NBLOCKS)
1385 		vap->va_nblocks = (u_longlong_t)st->st_blocks;
1386 
1387 	if (vap->va_mask & AT_SEQ)
1388 		vap->va_seq = 0;
1389 
1390 	return (0);
1391 }
1392 
1393 /* ARGSUSED */
1394 void
1395 flk_init_callback(flk_callback_t *flk_cb,
1396 	callb_cpr_t *(*cb_fcn)(flk_cb_when_t, void *), void *cbdata)
1397 {
1398 }
1399 
1400 void
1401 vn_hold(vnode_t *vp)
1402 {
1403 	mutex_enter(&vp->v_lock);
1404 	vp->v_count++;
1405 	mutex_exit(&vp->v_lock);
1406 }
1407 
1408 void
1409 vn_rele(vnode_t *vp)
1410 {
1411 	VERIFY3U(vp->v_count, !=, 0);
1412 	mutex_enter(&vp->v_lock);
1413 	if (vp->v_count == 1) {
1414 		mutex_exit(&vp->v_lock);
1415 		fop_inactive(vp, NULL, NULL);
1416 	} else {
1417 		vp->v_count--;
1418 		mutex_exit(&vp->v_lock);
1419 	}
1420 }
1421 
1422 int
1423 vn_has_other_opens(
1424 	vnode_t *vp,
1425 	v_mode_t mode)
1426 {
1427 
1428 	switch (mode) {
1429 	case V_WRITE:
1430 		if (vp->v_wrcnt > 1)
1431 			return (V_TRUE);
1432 		break;
1433 	case V_RDORWR:
1434 		if ((vp->v_rdcnt > 1) || (vp->v_wrcnt > 1))
1435 			return (V_TRUE);
1436 		break;
1437 	case V_RDANDWR:
1438 		if ((vp->v_rdcnt > 1) && (vp->v_wrcnt > 1))
1439 			return (V_TRUE);
1440 		break;
1441 	case V_READ:
1442 		if (vp->v_rdcnt > 1)
1443 			return (V_TRUE);
1444 		break;
1445 	}
1446 
1447 	return (V_FALSE);
1448 }
1449 
1450 /*
1451  * vn_is_opened() checks whether a particular file is opened and
1452  * whether the open is for read and/or write.
1453  *
1454  * Vnode counts are only kept on regular files (v_type=VREG).
1455  */
1456 int
1457 vn_is_opened(
1458 	vnode_t *vp,
1459 	v_mode_t mode)
1460 {
1461 
1462 	ASSERT(vp != NULL);
1463 
1464 	switch (mode) {
1465 	case V_WRITE:
1466 		if (vp->v_wrcnt)
1467 			return (V_TRUE);
1468 		break;
1469 	case V_RDANDWR:
1470 		if (vp->v_rdcnt && vp->v_wrcnt)
1471 			return (V_TRUE);
1472 		break;
1473 	case V_RDORWR:
1474 		if (vp->v_rdcnt || vp->v_wrcnt)
1475 			return (V_TRUE);
1476 		break;
1477 	case V_READ:
1478 		if (vp->v_rdcnt)
1479 			return (V_TRUE);
1480 		break;
1481 	}
1482 
1483 	return (V_FALSE);
1484 }
1485 
1486 /*
1487  * vn_is_mapped() checks whether a particular file is mapped and whether
1488  * the file is mapped read and/or write.
1489  */
1490 /* ARGSUSED */
1491 int
1492 vn_is_mapped(
1493 	vnode_t *vp,
1494 	v_mode_t mode)
1495 {
1496 	return (V_FALSE);
1497 }
1498